Interaction between probuf and client in go language

1, Environment configuration

  • 1,Download the installation package of the corresponding version

  • 2. Use the command go env to view your go path address and create a bin folder in the directory of go path

  • 3. Unzip the download from the first point and copy the protoc ol under bin to the bin folder under go path

  • 4. Check whether the installation was successful

    # View version
    protoc --version
    # View common commands
    protoc
    

    Currently only these languages are supported

  • 5. The configuration supports go language, Address of dependent package

    go get -u github.com/golang/protobuf/proto
    go get -u github.com/golang/protobuf/protoc-gen-go
    

2, Test probuf to go code

  • 1. Write a simple proto file

    //  helloWorld.proto
    
    // Version, just write 3 directly
    syntax = "proto3";
    // The package name converted to go. The preceding. Indicates that it is output in the current directory
    option go_package = ".;profile";
    
    message HelloWorld {
      string name = 1;
      int64 age = 2;
      double fee = 3;
      bool isLogin = 4;
    }
    
  • 2. Run the command to transfer the proto file to the go file

    # Protocol - I =. [current directory] - go_out=plugins=grpc:. [current directory] file name
    protoc -I=. --go_out=plugins=grpc:. helloWorld.proto
    

3, Testing

  • 1. Direct output

    package main
    
    import (
    	"fmt"
    	"go_demo/01.protobuf/proto"
    )
    
    func main() {
    	hello := proto.HelloWorld{
    		Name:    "Zhang San",
    		Age:     14,
    		Fee:     20.9,
    		IsLogin: false,
    	}
    	fmt.Println(&hello)
    }
    

4, Other data types

  • 1. Composite data types are actually data nesting

    ....
    message Location {
      double latitude = 1;
      double longitude = 2;
    }
    message HelloWorld {
      string name = 1;
      // Note that this place has nothing to do with the position, only with the following numbers
      Location location = 5;
      int64 age = 2;
      double fee = 3;
      bool isLogin = 4;
    }
    
  • 2. List data type

    message Location {
      double latitude = 1;
      double longitude = 2;
    }
    message HelloWorld {
      string name = 1;
      Location location = 5;
      int64 age = 2;
      double fee = 3;
      bool isLogin = 4;
      repeated Location locationList = 6;
    }
    
  • 3. Enumeration type

    // Define enumeration type
    enum HelloStatus {
      NO_PAY = 0;
      PAY = 1;
    }
    message HelloWorld {
      string name = 1;
      Location location = 5;
      int64 age = 2;
      double fee = 3;
      bool isLogin = 4;
      repeated Location locationList = 6;
      HelloStatus status = 7;
    }
    
  • 4. Assignment operation

    func main() {
    	var hello = proto.HelloWorld{
    		Name:    "Zhang San",
    		Age:     14,
    		Fee:     20.9,
    		IsLogin: false,
    		Location: &proto.Location{
    			Latitude:  30,
    			Longitude: 100,
    		},
    		LocationList: []*proto.Location{
    			{
    				Latitude:  100,
    				Longitude: 10,
    			},
    		},
        Status: profile.HelloStatus_NO_PAY,
    	}
    	fmt.Println(&hello)
    }
    

5, Other outputs

  • 1. Output binaries using proto.Marshal

    var hell1 profile.HelloWord
    err = proto.Unmarshal(b, &hell1)
    fmt.Println(&hello)
    
  • 2. Output json

    var hell1 profile.HelloWord
    b, err = json.Marshal(&hell1)
    fmt.Printf("%s\n", b)
    

6, Realize the data interaction between client and server

  • 1. Define a proto file

    // Version, just write 3 directly
    syntax = "proto3";
    // The package name converted to go. The preceding. Indicates that it is output in the current directory
    option go_package = ".;profile";
    
    message Location {
      double latitude = 1;
      double longitude = 2;
    }
    message HelloWorld {
      string name = 1;
      Location location = 5;
      int64 age = 2;
      double fee = 3;
      bool isLogin = 4;
      repeated Location locationList = 6;
    }
    
    message GetHelloRequest {
      string id = 1;
    }
    
    message GetHelloResponse {
      string id = 1;
      HelloWorld helloWorld = 2;
    }
    // This service must be defined
    service HelloService {
      rpc GetHello(GetHelloRequest) returns (GetHelloResponse);
    }
    
  • 2. Run the command to generate the go file, and view the service defined by yourself

    func (c *helloServiceClient) GetHello(ctx context.Context, in *GetHelloRequest, opts ...grpc.CallOption) (*GetHelloResponse, error) {
    	out := new(GetHelloResponse)
    	err := c.cc.Invoke(ctx, "/HelloService/GetHello", in, out, opts...)
    	if err != nil {
    		return nil, err
    	}
    	return out, nil
    }
    
  • 3. Define server-side methods

    type Service struct {
    }
    
    func (*Service) GetHello(ctx context.Context, req *profile.GetHelloRequest) (*profile.GetHelloResponse, error) {
    	return &profile.GetHelloResponse{
    		Id: req.Id,
    		HelloWorld: &profile.HelloWorld{
    			Name:    "Zhang San",
    			Age:     20,
    			Fee:     100,
    			IsLogin: true,
    			Location: &profile.Location{
    				Longitude: 100,
    				Latitude:  10,
    			},
    			LocationList: []*profile.Location{
    				{
    					Longitude: 10,
    					Latitude:  100,
    				},
    			},
    		},
    	}, nil
    }
    
  • 4. Method of starting server side

    func main() {
    	listen, err := net.Listen("tcp", ":9000")
    	if err != nil {
    		log.Fatalf("Server startup failed:%v", err)
    	}
    	// Start service
    	service :=grpc.NewServer()
      // Service is customized above
    	profile.RegisterHelloServiceServer(service, &Service{})
    	log.Fatal(service.Serve(listen))
    }
    
  • 5. Client startup

    package main
    
    import (
    	"context"
    	"fmt"
    	"go_demo/01.protobuf/profile"
    	"google.golang.org/grpc"
    	"log"
    )
    
    func main() {
    	conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure())
    	if err != nil {
    		log.Fatalf("Connection server error: %v", err)
    	}
    
    	client := profile.NewHelloServiceClient(conn)
    	response, err := client.GetHello(context.Background(), &profile.GetHelloRequest{
    		Id: "1001",
    	})
    	if err != nil {
    		log.Fatalf("Connection service error:%v", err)
    	}
    	fmt.Println(response)
    }
    

7, Convert protobuf to http request

  • 1. proto file before transformation

    syntax = "proto3";
    // Add this
    package collect;
    option go_package = ".;profile";
    
  • 2. Create a yaml file in the same proto directory

    # helloWorld.yaml file
    type: google.api.Service
    config_version: 3
    
    http:
      rules:
        - selector: collect.HelloService.GetHello
          get: /hello/{id}
    
  • 3. Install dependent packages

    go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
    
  • 4. Modify run command

    # It's the same as the previous command
    protoc -I=. --go_out=plugins=grpc,paths=source_relative:. helloWorld.proto
    # A helloWorld.pb.gw.go file will be generated
    protoc -I=. --grpc-gateway_out=paths=source_relative,grpc_api_configuration=helloWorld.yaml:. helloWorld.proto
    
  • 5. Because there are too many commands, it is inconvenient to remember to create a gen.sh file in the directory and put the two commands in it, which needs to be executed when running

    sh ./gen.sh
    
  • 6. The service layer of grpc is the same as that above

  • 7. Define http client

    package main
    
    import (
    	"context"
    	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    	"go_demo/01.protobuf/profile"
    	"google.golang.org/grpc"
    	"log"
    	"net/http"
    )
    
    func main() {
    	c := context.Background()
    	c, cancel := context.WithCancel(c)
    	defer cancel()
    
    	mux := runtime.NewServeMux()
    	err := profile.RegisterHelloServiceHandlerFromEndpoint(
    		c,
    		mux,
    		"localhost:9000", // grpc server address
    		[]grpc.DialOption{grpc.WithInsecure()},
    	)
    	if err != nil {
    		log.Fatalf("connect grpc Error:%v", err)
    	}
    	err = http.ListenAndServe(":8080", mux)
    	if err != nil {
    		log.Fatalf("Listening error:%v", err)
    	}
    }
    
  • 8. Enter directly on the browser http://localhost:8080/hello/123

8, The processing back end returns the data structure of the front end

There are two problems

  • 1. The age field is originally defined as an integer, and the character type is returned to the front end
  • 2. The status field should return the value of enumeration type, not string

terms of settlement

  • 1. The age field changes the previous int64 to the current int32 data type by modifying the data type

  • 2. The status enumeration type can be modified through the http client

    ...
    var mux = runtime.NewServeMux(
      runtime.WithMarshalerOption(
        runtime.MIMEWildcard,
        &runtime.JSONPb{
          MarshalOptions: protojson.MarshalOptions{
            UseEnumNumbers: true, // Enumeration field values use numbers
            UseProtoNames:  true,
          },
          UnmarshalOptions: protojson.UnmarshalOptions{
            DiscardUnknown: true, // Ignore the nonexistent poroto field sent by the client
          },
        },
      ),
    )
    ...
    

Tags: Go

Posted on Tue, 05 Oct 2021 18:09:36 -0400 by newbeee