2020年8月15日 星期六

[ 文章收集 ] gRPC - A basic tutorial introduction to gRPC in Go

 Source From Here

Preface
This tutorial provides a basic Go programmer’s introduction to working with gRPC. By walking through this example you’ll learn how to:
* Define a service in a .proto file.
* Generate server and client code using the protocol buffer compiler.
* Use the Go gRPC API to write a simple client and server for your service.

It assumes that you have read the Introduction to gRPC and are familiar with protocol buffers. Note that the example in this tutorial uses the proto3 version of the protocol buffers language: you can find out more in the proto3 language guide and the Go generated code guide.

Why use gRPC?
Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients.

With gRPC we can define our service once in a .proto file and generate clients and servers in any of gRPC’s supported languages, which in turn can be run in environments ranging from servers inside a large data center to your own tablet — all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating.

Example code and setup
The example code for our tutorial is in grpc/grpc-go/examples/route_guide. To download the example, clone the grpc-go repository by running the following command:
# go get google.golang.org/grpc

Then change your current directory to grpc-go/examples/route_guide
# cd $GOPATH/src/google.golang.org/grpc/examples/route_guide
# ls
client README.md routeguide server testdata

You also should have the relevant tools installed to generate the server and client interface code - if you don’t already, follow the setup instructions in the Go quick start guide.

Defining the service
Our first step (as you’ll know from the Introduction to gRPCis to define the gRPC service and the method request and response types using protocol buffers. You can see the complete .proto file in examples/route_guide/routeguide/route_guide.proto.

To define a service, you specify a named service in your .proto file:
  1. service RouteGuide {  
  2.    ...  
  3. }  
Then you define rpc methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the RouteGuide service:

A simple RPC where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call:
  1. // Obtains the feature at a given position.  
  2. rpc GetFeature(Point) returns (Feature) {}  
A server-side streaming RPC where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the stream keyword before the response type.
  1. // Obtains the Features available within the given Rectangle.  Results are  
  2. // streamed rather than returned at once (e.g. in a response message with a  
  3. // repeated field), as the rectangle may cover a large area and contain a  
  4. // huge number of features.  
  5. rpc ListFeatures(Rectangle) returns (stream Feature) {}  
A client-side streaming RPC where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a client-side streaming method by placing the stream keyword before the request type.
  1. // Accepts a stream of Points on a route being traversed, returning a  
  2. // RouteSummary when traversal is completed.  
  3. rpc RecordRoute(stream Point) returns (RouteSummary) {}  
A bidirectional streaming RPC where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the stream keyword before both the request and the response.
  1. // Accepts a stream of RouteNotes sent while a route is being traversed,  
  2. // while receiving other RouteNotes (e.g. from other users).  
  3. rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}  
Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here’s the Point message type:
  1. // Points are represented as latitude-longitude pairs in the E7 representation  
  2. // (degrees multiplied by 10**7 and rounded to the nearest integer).  
  3. // Latitudes should be in the range +/- 90 degrees and longitude should be in  
  4. // the range +/- 180 degrees (inclusive).  
  5. message Point {  
  6.   int32 latitude = 1;  
  7.   int32 longitude = 2;  
  8. }  
Example of Protocol Buffer: person.proto
For the materials here, it is from this lesson "Getting Started with Protocol Buffers in Go - Tutorial". So in order to know protocol buffer well, let's create one .proto as below:
person/person.proto
  1. syntax = "proto3";  
  2.   
  3. package person;  
  4.   
  5. message SocialFollowers {  
  6.     int32 youtube = 1;  
  7.     int32 facebook = 2;  
  8. };  
  9.   
  10. message Person {  
  11.     string name = 1;  
  12.     int32 age = 2;  
  13.     SocialFollowers socialFollowers = 3;  
  14. }  
Then we can build it this way:
# protoc --go_out=plugins=grpc:. person/person.proto
# vi person/person.pb.go // Check the content of generated file

Now let's check how to marsh/unmarsh protocol boffer:
main.go
  1. package main  
  2.   
  3. import (  
  4.     "fmt"  
  5.     "log"  
  6.     person "proto_go_ex/person"  
  7.     proto "github.com/golang/protobuf/proto"  
  8. )  
  9.   
  10. func main(){  
  11.     fmt.Println("Hello world!")  
  12.   
  13.     john := &person.Person{  
  14.         Name: "John",  
  15.         Age: 24,  
  16.         SocialFollowers: &person.SocialFollowers {  
  17.             Youtube: 1,  
  18.             Facebook: 5,  
  19.         },  
  20.     }  
  21.   
  22.     // Marshal data  
  23.     data, err := proto.Marshal(john)  
  24.     if err != nil {  
  25.         log.Fatalf("Marshalling error: %v\n", err)  
  26.     }  
  27.   
  28.     fmt.Println(data)  
  29.   
  30.     // Unmarshal data  
  31.     newPerson := &person.Person{}  
  32.     err = proto.Unmarshal(data, newPerson)  
  33.     if err != nil {  
  34.         log.Fatal("Unmarshalling error:", err)  
  35.     }  
  36.   
  37.     fmt.Println(newPerson)  
  38. }  
Finally, let's execute it this way:
# go run main.go
Hello world!
[10 4 74 111 104 110 16 24 26 4 8 1 16 5]
name:"John" age:24 socialFollowers:{youtube:1 facebook:5}

Creating Protocol Buffer .proto
The lesson from now on is actually the materials of this video "Beginners Guide to gRPC in Go!". Before all, let's get the necessary packages:
# go get github.com/golang/protobuf
# go get github.com/golang/protobuf/proto
# go get -u github.com/golang/protobuf/protoc-gen-go

Then to install protoc (reference):
# PROTOC_ZIP=protoc-3.7.1-linux-x86_64.zip
# curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.7.1/$PROTOC_ZIP
# sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
# sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*'
# rm -f $PROTOC_ZIP

Ok, now the first step to create gRPC service, we need to create our .proto file:
chat/chat.proto
  1. syntax = "proto3";  
  2. package chat;  
  3.   
  4. message Message {  
  5.     string body = 1;  
  6. }  
  7.   
  8. service ChatService {  
  9.     rpc SayHello(Message) returns (Message) {}  
  10. }  
Then we can build it by command below:
# protoc chat/chat.proto --go_out=plugins=grpc:.
# vi chat/chat.pb.go // Check the created content

Now let's create another go file for convenience of future development:
chat/chat.go
  1. package chat  
  2.   
  3. import (  
  4.     "log"  
  5.     "golang.org/x/net/context"  
  6. )  
  7.   
  8. type Server struct {  
  9. }  
  10.   
  11. func (s *Server) SayHello(ctx context.Context, message *Message) (*Message, error) {  
  12.     log.Printf("Receive message body from client: %s", message.Body)  
  13.     return &Message{Body: "Hello from the server"}, nil  
  14. }  
From above, we implement the function SayHello as if it is a service provided in the server. We will see how to integrate it in server side later.

Generating client and server code
Now let's create code for server side:
server.go
  1. package main  
  2.   
  3. import (  
  4.     "log"  
  5.     "net"  
  6.     "google.golang.org/grpc"  
  7.     pb "proto_go_ex/chat"  
  8. )  
  9.   
  10. func main(){  
  11.     lis, err := net.Listen("tcp"":9000")  
  12.     if err != nil {  
  13.         log.Fatalf("Fail to listen on port 9000: %v", err)  
  14.     }  
  15.   
  16.     grpcServer := grpc.NewServer()  
  17.     s := pb.Server{}  
  18.     pb.RegisterChatServiceServer(grpcServer, &s)  
  19.   
  20.     if err := grpcServer.Serve(lis); err != nil {  
  21.         log.Fatalf("Failed to serve gRPC server over port 9000: %v", err)  
  22.     }  
  23.   
  24. }  
Then we can launch the server by command below:
# go run server.go

Then below is for the code of client:
client.go
  1. package main  
  2.   
  3. import (  
  4.     "log"  
  5.     "golang.org/x/net/context"  
  6.     "google.golang.org/grpc"  
  7.     pb "proto_go_ex/chat"  
  8. )  
  9.   
  10. func main(){  
  11.     var conn *grpc.ClientConn  
  12.     conn, err := grpc.Dial(":9000", grpc.WithInsecure())  
  13.     if err != nil {  
  14.         log.Fatalf("could not connect: %s", err)  
  15.     }  
  16.   
  17.     defer conn.Close()  
  18.     c := pb.NewChatServiceClient(conn)  
  19.     message := pb.Message {  
  20.         Body: "Hello from the client!",  
  21.     }  
  22.     resp, err := c.SayHello(context.Background(), &message)  
  23.     if err != nil {  
  24.         log.Fatalf("Error when calling SayHello: %s", err)  
  25.     }  
  26.   
  27.     log.Printf("Resp from Server: %s", resp.Body)  
  28. }  
Then we can execute the client to call the service "SayHello" from server side:
# go run client.go
2020/08/15 03:42:49 Resp from Server: Hello from the server

From the server side, you can see below similar message too:
2020/08/15 03:42:49 Receive message body from client: Hello from the client!

For all the source codes mentioned above, you can refer to 
this repo.

Supplement
Getting Started with Protocol Buffers in Go - Tutorial
Go Protobuf Tips
Golang Development - Beginners Guide to gRPC in Go (1)!
Golang gRPC 教學 - part 1

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...