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 }, }, ), ) ...