go microservice - RPC combined with Protobuf

RPC in combination with Protobuf



Here we use a demand scenario to learn relevant knowledge points:

Suppose there is an Order module in a system. Other modules want to realize the remote project call of RPC. The Order information can be obtained according to the Order ID and timestamp. If the acquisition is successful, the corresponding Order information will be returned; If the query fails, the failure information is returned. Now, let's program the requirements.


Definition of transmission data format


Data definition

Define message according to requirements Proto file, which is defined in detail as follows:

syntax = "proto3";
package message;
option go_package="./;message";

//Order request parameters
message OrderRequest {
    string orderId = 1;
    int64 timeStamp = 2;
}

//Order information
message OrderInfo {
    string OrderId = 1;
    string OrderName = 2;
    string OrderStatus = 3;
}

In the above file, the request data structure OrderRequest when the client initiates an RPC call and the data structure OrderInfo returned after the server queries are defined. The data definition is implemented in proto3 syntax, and the whole data definition is defined under the message package.

  • Compiling proto files

    Compile the command through proto The proto file is compiled to automatically generate the Go language file of the corresponding structure. The compilation command is as follows:

    protoc ./message.proto --go_out=./
    

    The above command is executed under the message package. After the compilation command is completed, message will be generated under the message package pb. Go file, which automatically generates the definitions and related methods of OrderRequest and OrderInfo structures in go language.

Combination of Protobufg format data and RPC

  • Definition of service
    To call RPC remote procedure and realize the method of calling remote server, there must be service first. In this case, the service providing order query function is defined, named OrderService, and the order information query method is provided for remote call. The detailed services and methods are defined as follows:

    //Order service
    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: "clothes", OrderStatus: "Paid"},
    		"201907310001": message.OrderInfo{OrderId: "201907310001", OrderName: "snacks", OrderStatus: "Paid"},
    		"201907310002": message.OrderInfo{OrderId: "201907310002", OrderName: "food", OrderStatus: "Unpaid"},
    	}
    
     current := time.Now().Unix()
     if (request.TimeStamp > current) {
      *response = message.OrderInfo{OrderId: "0", OrderName: "", OrderStatus: "Abnormal order information"}
     } else {
      result := orderMap[request.OrderId]//201907310003
      if result.OrderId != "" {
    	 *response = orderMap[request.OrderId]
      } else {
    	 return errors.New("server error")
      }
     }
     return nil
    }
    

    In the method definition of service, orderMap is used to simulate the initial order database to facilitate case query and display. The GetOrderInfo method has two parameters. The first is message Orderrequest, as the parameter passed by the caller, the second is message Orderinfo, as the parameter returned by the call, passes the above two parameters proto defines and automatically generates Go language structure data.

  • Registration and processing of services
    After the service is defined, you need to register the service with the RPC framework and enable http request listening and processing. This part of the code is consistent with the previous RPC server implementation logic. The specific implementation is as follows:

    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 client call implementation
    On the client side, in addition to the logic that the client normally accesses the remote server, it is also necessary to prepare the request data message that the client needs to pass OrderInfo. The specific implementation is as follows:

    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)
    

Keywords: Microservices

Added by admin on Mon, 28 Feb 2022 03:08:44 +0200