RPC 与 HTTP 的区别
在微服务架构中,RPC(远程过程调用)和 HTTP 是两种常见的通信协议,它们在接口定义和使用方式上有显著差异。
接口定义与约束
RPC 通过 Proto 文件(Protocol Buffers)严格约束接口行为。在 Proto 文件中,开发者可以精确定义:
- 接口的参数类型和结构
- 返回值的具体格式
- 方法的命名规范
相比之下,HTTP 协议本身对接口行为没有强制性约束。服务提供方和调用方需要:
- 自行约定接口规范
- 手动实现错误处理逻辑
- 维护接口文档和通信协议
RPC 实现:以 Go 语言 gRPC 为例
服务端实现步骤
- 定义 Protobuf 服务接口
1 2 3 4
| service OrderService { rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) {} rpc GetOrder(GetOrderRequest) returns (GetOrderResponse) {} }
|
- 实现服务接口
1 2 3 4 5 6 7 8 9 10 11 12
| type OrdersGrpcHandler struct { orderService types.OrderService }
func (h OrdersGrpcHandler) CreateOrder(ctx context.Context, req *orders.CreateOrderRequest) (*orders.CreateOrderResponse, error) { order, err := h.orderService.Create(req) if err != nil { return nil, status.Errorf(codes.Internal, "创建订单失败: %v", err) } return &orders.CreateOrderResponse{OrderId: order.ID}, nil }
|
- 注册 gRPC 服务
1 2 3 4 5 6
| func NewGrpcOrdersService(grpcServer *grpc.Server, orderService types.OrderService) { handler := &OrdersGrpcHandler{ orderService: orderService, } orders.RegisterOrderServiceServer(grpcServer, handler) }
|
- 启动 gRPC 服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func (s *gRPCServer) Run() error { listener, err := net.Listen("tcp", s.addr) if err != nil { return fmt.Errorf("监听失败: %v", err) }
grpcServer := grpc.NewServer() orderService := service.NewOrderService() NewGrpcOrdersService(grpcServer, orderService) log.Printf("gRPC 服务器启动,监听地址:%s", s.addr) return grpcServer.Serve(listener) }
|
客户端实现步骤
- 创建 gRPC 连接
1 2 3 4 5 6 7 8 9 10 11
| func NewGRPCClient(addr string) (*grpc.ClientConn, error) { conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), grpc.WithTimeout(5*time.Second), ) if err != nil { return nil, fmt.Errorf("连接服务器失败: %v", err) } return conn, nil }
|
- 调用远程方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func (s *httpServer) createOrder() error { conn, err := NewGRPCClient(":9000") if err != nil { return err } defer conn.Close()
client := orders.NewOrderServiceClient(conn) resp, err := client.CreateOrder(context.Background(), &orders.CreateOrderRequest{ CustomerId: "001", ProductId: "001", Quantity: 1, }) if err != nil { return fmt.Errorf("创建订单失败: %v", err) }
log.Printf("订单创建成功,订单ID:%s", resp.OrderId) return nil }
|
RPC 的关键优势
- 强类型接口:通过 Proto 文件定义严格的接口契约
- 高性能:使用二进制传输,通信开销低
- 跨语言支持:可以生成多种编程语言的客户端和服务端代码
- 内置服务发现和负载均衡