I. Environment configuration

  • 1. Download the installation package of the corresponding version

  • 2. Run the go env command to check your go path address and create a bin folder under the go path directory

  • 3, unzip the downloads from the first point and copy the protoc under bin to the bin folder under go path

  • 4. Check whether the installation is successful

    # check version
    protoc --version
    # Check common commands
    protoc
    Copy the code

    Currently, only these languages are supported

  • 5. Configure to support go language and depend on package address

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

Second, the testprobufturngocode

  • 1. Write simple Proto files

    // helloWorld.proto
    
    // Version, just stick to 3
    syntax = "proto3";
    // Convert to the package name of go, preceding. Output in the current directory
    option go_package = ".; profile";
    
    message HelloWorld {
      string name = 1;
      int64 age = 2;
      double fee = 3;
      bool isLogin = 4;
    }
    Copy the code
  • 2. Run commands to convert the proto file to the go file

    #Protoc -i =.[current directory] --go_out=plugins= GRPC :.[current directory] File name
    protoc -I=. --go_out=plugins=grpc:. helloWorld.proto
    Copy the code

Three, test,

  • 1. Direct output

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

Other data types

  • 1. Compound data types, in fact, are nested data

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

    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;
    }
    Copy the code
  • 3. Enumeration types

    // Define an 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;
    }
    Copy the code
  • 4, import another file

    A similar type is used in multiple proto files. You don’t need to define a type in each proto file. You just need to define a common type and import it in other files

    // base.proto
    syntax = "proto3";
    
    message Empty {}
    Copy the code
    syntax = "proto3";
    import "base.proto";
    option go_package=".; proto";
    
    service Hello {
      rpc SayHello(Empty) returns (Empty);
    }
    Copy the code
  • 5. Introduce built-in packages

    syntax = "proto3";
    import "google/protobuf/empty.proto";
    option go_package=".; proto";
    
    service Hello {
      rpc SayHello(google.protobuf.Empty) returns (google.protobuf.Empty);
    }
    Copy the code
  • 6. About other built-in

  • 7. Map type

    message HelloWorld {... map<string.string> mp = 8;
    }
    Copy the code
  • 8. Built-in time types

    syntax = "proto3";
    import "google/protobuf/timestamp.proto";
    
    message HelloWorld {... google.protobuf.Timestamp addTime =9;
    }
    Copy the code
  • 9. Assignment

    func main(a) {
    	var hello = proto.HelloWorld{
    		Name:    "Zhang",
    		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)
    }
    Copy the code
  • 10. Assignment of time

    import (
    	"google.golang.org/protobuf/types/known/timestamppb"
    	"time"
    )
    
    var hello = profile.HelloWorld{
      ...
      AddTime: timestamppb.New(time.Now()),
    }
    Copy the code

5. Other outputs

  • 1. Use proto.Marshal to output the binary

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

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

Realize data interaction between client and server

  • 1. Define a proto file

    // Version, just stick to 3
    syntax = "proto3";
    // Convert to the package name of go, preceding. 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;
    }
    // The service must be defined
    service HelloService {
      rpc GetHello(GetHelloRequest) returns (GetHelloResponse);
    }
    Copy the code
  • 2. Run the command to generate the go file and view the service you define

    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...)
    	iferr ! =nil {
    		return nil, err
    	}
    	return out, nil
    }
    Copy the code
  • 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",
    			Age:     20,
    			Fee:     100,
    			IsLogin: true,
    			Location: &profile.Location{
    				Longitude: 100,
    				Latitude:  10,
    			},
    			LocationList: []*profile.Location{
    				{
    					Longitude: 10,
    					Latitude:  100,},},},},nil
    }
    Copy the code
  • 4. Start the server-side method

    func main(a) {
    	listen, err := net.Listen("tcp".": 9000")
    	iferr ! =nil {
    		log.Fatalf("Server startup failure :%v", err)
    	}
    	// Start the service
    	service :=grpc.NewServer()
      // Service is custom from above
    	profile.RegisterHelloServiceServer(service, &Service{})
    	log.Fatal(service.Serve(listen))
    }
    Copy the code
  • 5. Start the client

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

Seven,protobufconverthttprequest

  • 1. Transform the proTO file before

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

    # the helloWorld. Yaml files
    type: google.api.Service
    config_version: 3
    
    http:
      rules:
        - selector: collect.HelloService.GetHello
          get: /hello/{id}
    Copy the code
  • 3. Install dependency packages

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

    #It's the same order as before
    protoc -I=. --go_out=plugins=grpc,paths=source_relative:. helloWorld.proto
    #A helloWorld.pb.gw.go file is generated
    protoc -I=. --grpc-gateway_out=paths=source_relative,grpc_api_configuration=helloWorld.yaml:. helloWorld.proto
    Copy the code
  • 5. Since there are too many commands to remember, create a gen.sh file in the directory, put the two commands in it, and execute them when necessary

    sh ./gen.sh
    Copy the code
  • 6. The service layer of GRPC is the same as above

  • 7. Define the 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(a) {
    	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()},
    	)
    	iferr ! =nil {
    		log.Fatalf("Error connecting GRPC: %v", err)
    	}
    	err = http.ListenAndServe(": 8080", mux)
    	iferr ! =nil {
    		log.Fatalf("Listening error: %v", err)
    	}
    }
    Copy the code
  • 8, directly enter http://localhost:8080/hello/123 in your browser

Processing the data structure of the back end back to the front end

There are two problems

  • 1. We originally defined the age field as an integer and returned the character type to the front end
  • We should return an enumerated value, not a string

The solution

  • 1. Change the datatype of the age field from INT64 to int32

  • 2. The state enumeration type is modified via 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 client},},),)...Copy the code