RPC与Protobuf结合使用
这里我们利用一个需求场景来学习相关的知识点:
假设在一个系统中,有订单模块(Order),其他模块想要实现RPC的远程工程调用,根据订单ID和时间戳可以获取订单信息。如果获取成功就返回相应的订单信息;如果查询不到返回失败信息。现在,我们来进行需求的编程实现。
传输数据格式定义
数据定义
根据需求,定义message.proto文件,详细定义如下:
syntax = "proto3";
package message;
option go_package="./;message";
//订单请求参数
message OrderRequest {
string orderId = 1;
int64 timeStamp = 2;
}
//订单信息
message OrderInfo {
string OrderId = 1;
string OrderName = 2;
string OrderStatus = 3;
}
在上述文件中,定义了客户端发起RPC调用时的请求数据结构OrderRequest和服务端查询后返回的数据结构OrderInfo。数据定义采用proto3语法实现,整个数据定义被定义在message包下。
-
编译proto文件
通过proto编译命令对.proto文件进行编译,自动生成对应结构体的Go语言文件。编译命令如下:
protoc ./message.proto --go_out=./
执行上述命令是在message包下。编译命令结束后,会在message包下生成message.pb.go文件,其中自动生成了OrderRequest和OrderInfo在Go语言中结构体的定义和相关的方法。
Protobufg格式数据与RPC结合
-
服务的定义
进行RPC远程过程调用,实现调用远程服务器的方法,首先要有服务。在本案例中,定义提供订单查询功能的服务,取名为OrderService,同时提供订单信息查询方法供远程调用。详细的服务和方法定义如下://订单服务 type OrderService struct { } func (os *OrderService) GetOrderInfo(request message.OrderRequest, response *message.OrderInfo) error { //201907310003 orderMap := map[string]message.OrderInfo{ "201907300001": message.OrderInfo{OrderId: "201907300001", OrderName: "衣服", OrderStatus: "已付款"}, "201907310001": message.OrderInfo{OrderId: "201907310001", OrderName: "零食", OrderStatus: "已付款"}, "201907310002": message.OrderInfo{OrderId: "201907310002", OrderName: "食品", OrderStatus: "未付款"}, } current := time.Now().Unix() if (request.TimeStamp > current) { *response = message.OrderInfo{OrderId: "0", OrderName: "", OrderStatus: "订单信息异常"} } else { result := orderMap[request.OrderId]//201907310003 if result.OrderId != "" { *response = orderMap[request.OrderId] } else { return errors.New("server error") } } return nil }
在服务的方法定义中,使用orderMap模拟初始订单数据库,方便案例查询展示。GetOrderInfo方法有两个参数,第一个是message.OrderRequest,作为调用者传递的参数,第二个是message.OrderInfo,作为调用返回的参数,通过此处的两个参数,将上文通过.proto定义并自动生成的Go语言结构体数据结合起来。
-
服务的注册和处理
服务定义好以后,需要将服务注册到RPC框架,并开启http请求监听处理。这部分代码与之前的RPC服务端实现逻辑一致,具体实现如下:func main() { orderService := new(OrderService) rpc.Register(orderService) rpc.HandleHTTP() listen, err := net.Listen("tcp", ":8081") if err != nil { panic(err.Error()) } http.Serve(listen, nil) }
-
RPC客户端调用实现
在客户端,除了客户端正常访问远程服务器的逻辑外,还需要准备客户端需要传递的请求数据message.OrderInfo。具体实现如下:client, err := rpc.DialHTTP("tcp", "localhost:8081") if err != nil { panic(err.Error()) } timeStamp := time.Now().Unix() request := message.OrderRequest{OrderId: "201907310001", TimeStamp: timeStamp} var response *message.OrderInfo err = client.Call("OrderService.GetOrderInfo", request, &response) if err != nil { panic(err.Error()) } fmt.Println(*response)