Introduction to GRPC Golang development

Source: Internet
Author: User
Tags sprintf
This is a creation in Article, where the information may have evolved or changed.

Mainly based on the official website introduction of the summary of the document.

Need to know protocol buffers first

Why use Grpc

With GPRC, we can define a service to the. proto file only once, and then develop the client or server using any development language supported by GRPC.

Sample code and setting up the environment

The first step is to ensure that the Golang development environment is properly configured and go1.5+.

$ go get -u -v  google.golang.org/grpc

I encountered an error in the test, the main reason is that the sample needs

    "golang.org/x/net"    "golang.org/x/text"

Support, my solution is as follows
To

 $GOPATH/src/golang.org/x/

Directory, if golang.org/x/does not exist, create one manually.
And then

git clone https://github.com/golang/net.gitgit clone https://github.com/golang/text.git

Sample test

$ cd $GOPATH/src/google.golang.org/grpc/examples/route_guide$ go run server/server.go$ go run client/client.go

The following is an analysis of the Code of the sample

Service definition

GRPC uses protocol buffers to define the service.
To define the service, you need to define the services in the. proto file as follows:

service RouteGuide {   ...}

You can then define the RPC method in Servie, specifying the corresponding request and response type. GPRC allows the developer to define the service method in 4, which is useful in the 4 method in the sample Routeguide.

  • The simplest RPC method, the client sends a request to the server by calling the method, waits for the response of the servers, similar to a normal function call.

    // Obtains the feature at a given position.rpc GetFeature(Point) returns (Feature) {}
  • RPC (Server-side streaming RPC) with one-side stream on the server: The client calls the method to the service side, the servers return a stream, and the client reads the data from the stream until there is no data to read. As you can see from the sample code, the main feature of this method is to add a stream before the response type.

    // Obtains the Features available within the given Rectangle.  Results are// streamed rather than returned at once (e.g. in a response message with a// repeated field), as the rectangle may cover a large area and contain a// huge number of features.rpc ListFeatures(Rectangle) returns (stream Feature) {}
  • Client side stream RPC (A client-side streaming RPC): The client sends a series of data to the server by using the stream. After the client data is sent, it waits for the server to read all the data and send it accordingly. As you can see from the sample code, the main feature of this method is to add a stream before the request type.:

    // Accepts a stream of Points on a route being traversed, returning a// RouteSummary when traversal is completed.rpc RecordRoute(stream Point) returns (RouteSummary) {}
  • Bilateral stream RPC (bidirectional streaming RPC). Both the client and the server send a series of messages to each other through a read-write stream (Read-write stream). These two streams are completely independent, so the client and server can freely read and write operations: for example, the server can wait for the client after the data are received and then write data to the response, or you can read a message before writing a piece of information or some other reading and writing combination. As you can see from the sample code, the main feature of this method is to add a stream before the request and response.
// Accepts a stream of RouteNotes sent while a route is being traversed,// while receiving other RouteNotes (e.g. from other users).rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

The. proto file in the sample contains the protocol pool message type definition for the type used by the request and response types used in the service-side method (protocol buffer, type definitions).

// Points are represented as latitude-longitude pairs in the E7 representation// (degrees multiplied by 10**7 and rounded to the nearest integer).// Latitudes should be in the range +/- 90 degrees and longitude should be in// the range +/- 180 degrees (inclusive).message Point {  int32 latitude = 1;  int32 longitude = 2;}

Generate client and server-side code

Generate the Grpc interface code required by the client and server based on the. Proto file

protoc -I routeguide/ routeguide/route_guide.proto --go_out=plugins=grpc:routeguide

Create a service-side

The service-side code works in two main ways:

    • Implement the previous step. Proto the generated service-side interface.
    • Run a GRPC service to listen to the client's requests and distribute the request to the correct service-side implementation.

Implement Routeguide

As can see, we have a routeguideserver struct type that implements the generated Routeguideserver interface:
We can see that there is a routeguideserver structure type in our service to implement the Routeguideserver interface.

type routeGuideServer struct {        ...}...func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {        ...}...func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {        ...}...func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {        ...}...func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {        ...}...

Simple RPC

Getfeature, gets a point from the client and then returns the corresponding feature information from the database.

func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {    for _, feature := range s.savedFeatures {        if proto.Equal(feature.Location, point) {            return feature, nil        }    }    // No feature was found, return an unnamed feature    return &pb.Feature{"", point}, nil}

This method input parameter is an RPC context object and a Point protocol buffer request that the client sends over. This method returns a feature protocol pool (Feature protocol Buffer) object that contains the response information and errors. In this method, we transfer the correct information for feature and then return with nil error, telling the GRPC that the server has finished processing the RPC, and feature can return it to the client.

Server-side Streaming RPC

Listfeatures is a service-side stream of RPC, so we need to return multiple features to the client.

func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {    for _, feature := range s.savedFeatures {        if inRange(feature.Location, rect) {            if err := stream.Send(feature); err != nil {                return err            }        }    }    return nil}

As you can see, this method gets a request object and a special routeguide_listfeaturesserver to write accordingly. In this method we use the Send method to write all feature features that need to be returned to the routeguide_listfeaturesserver. Finally returns a nil error telling the GRPC that the server has been written appropriately. If any errors occur during the period, we return a non-nil error,grpc that will be converted to the correct RPC status to be sent to the line.

Client-side Streaming RPC

.
Client Flow Method Recordroute, we get a series of point from the client and then return a Routesummary object containing travel information. You can see from the code that the method does not have any request parameters, but rather a routeguide_recordrouteserver stream object. The server can use the Rev () method to read the message from the Routeguide_recordrouteserver object and use the Write () method to write the message inside.

  func (S *routeguideserver) Recordroute (stream PB. Routeguide_recordrouteserver) Error {var pointcount, Featurecount, distance int32 var lastpoint *pb. Point startTime: = time. Now () for {point, err: = stream. RECV () If err = = Io. EOF {endTime: = time. Now () return stream. Sendandclose (&PB.      routesummary{Pointcount:pointcount, Featurecount:featurecount, Distance: Distance, Elapsedtime:int32 (Endtime.sub (startTime).  Seconds ()),})} if err! = Nil {return err} pointcount++ for _, Feature: = Range S.savedfeatures {if Proto. Equal (feature.  Location, point) {featurecount++}} if Lastpoint! = Nil {distance + = Calcdistance (Lastpoint, point)} Lastpoint = point}}  

In this method, we use Routeguide_recordrouteserver's Recv method to continuously read data from the client's request to the Requesst object until there is no data to read. The server needs to detect each recv return error, if it is nil, indicating that the stream can continue to read normally, if it is IO. EOF indicates that the stream has stopped and the server can return routesummary.

Bidirectional streaming RPC

func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {    for {        in, err := stream.Recv()        if err == io.EOF {            return nil        }        if err != nil {            return err        }        key := serialize(in.Location)                ... // look for notes to be sent to client        for _, note := range s.routeNotes[key] {            if err := stream.Send(note); err != nil {                return err            }        }    }}

This method uses the Routeguide_routechatserver stream object, which can be used to read messages and write messages. However, this time we return data through the stream while the client is still writing messages to their message stream.
This method uses the Send () method instead of Sendandclose () to write messages to the message flow.
The reasons for this are as follows: The specific meaning is not understood for the time being.

Todo:the syntax for reading and writing here's very similar to our client-streaming method, except the server uses the St Ream ' s Send () method rather than sendandclose () because it ' s writing multiple responses. Although each side'll always get the other's messages in the order they were written, both the client and server can Rea D and write in any order-the streams operate completely independently.

Starting the server

flag.Parse()lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))if err != nil {        log.Fatalf("failed to listen: %v", err)}grpcServer := grpc.NewServer()pb.RegisterRouteGuideServer(grpcServer, &routeGuideServer{})... // determine whether to use TLSgrpcServer.Serve(lis)

As the code shows, we need the following 4 steps to create and start a server:

    • Specify a port number to listen for client requests, using the
      err := net.Listen("tcp", fmt.Sprintf(":%d", *port)).
      • Create a GRPC server instance
        grpc.NewServer().
    • The registration server is implemented on the GRPC server instance created in the previous step.

    • Call serve to start the service, blocking the wait until the process is killed or the server's stop is called.

Using TLS

func main() {    flag.Parse()    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))    if err != nil {        grpclog.Fatalf("failed to listen: %v", err)    }    var opts []grpc.ServerOption    if *tls {        creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)        if err != nil {            grpclog.Fatalf("Failed to generate credentials %v", err)        }        opts = []grpc.ServerOption{grpc.Creds(creds)}    }    grpcServer := grpc.NewServer(opts...)    pb.RegisterRouteGuideServer(grpcServer, newServer())    grpcServer.Serve(lis)}

Creating the Client

Create Client

  flag. Parse () var opts []grpc.        Dialoption if *tls {var sn string if *serverhostoverride! = "" {sn = *serverhostoverride } var creds credentials. Transportcredentials if *cafile! = "" {var err error creds, err = credentials. Newclienttlsfromfile (*cafile, SN) if err! = Nil {grpclog. Fatalf ("Failed to create TLS credentials%v", err)}} else {creds = credentials. Newclienttlsfromcert (nil, sn)} opts = Append (opts, Grpc. Withtransportcredentials (creds))} else {opts = append (opts, Grpc. Withinsecure ())} conn, err: = Grpc.    Dial (*serveraddr, opts ...) If err! = Nil {grpclog. Fatalf ("Fail to dial:%v", err)} defer conn. Close () Client: = PB. Newrouteguideclient (conn)  

To be able to invoke the service-side approach, we first create a GRPC channel to communicate with the server. Give Grpc by passing in the server address and port number. Dial () to create. As with the code, we can also use Dialoptions to set the authentication method in the Grpc.
Once the GRPC channel is set up, we need a client to execute the RPC, created by the Newrouteguideclient method provided in the PB package created by the. Proto.

Calling service methods

There are four methods that correspond to the service side, and the client has to use different calling methods.

Simple RPC

feature, err := client.GetFeature(context.Background(), &pb.Point{409146138, -746188906})if err != nil {        ...}

As seen from the code, the client invokes the method Getfeature (in), passing the Protocol pool (Protocol buffer object) to the PB. Point as a parameter, passing a context at the same time. The Context object allows us to easily change RPC behavior, such as timeouts or canceling RPCs.

Server-side Streaming RPC

rect := &pb.Rectangle{ ... }  // initialize a pb.Rectanglestream, err := client.ListFeatures(context.Background(), rect)if err != nil {    ...}for {    feature, err := stream.Recv()    if err == io.EOF {        break    }    if err != nil {        log.Fatalf("%v.ListFeatures(_) = _, %v", client, err)    }    log.Println(feature)}

Cient. Listfeaturens see. Proto generated Route_guide.pb.go

func (c *routeGuideClient) ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error) {

In this method, the same context object and a request are passed, but a routeguide_listfeaturesclient instance is returned, and the client can read from this instance the response from the server.
We use the Routeguide_listfeaturesclient recv method to read from the response of the server to the Protocol Pool object feature until there is no data to read. The same client needs to detect the returned err if it is nil, stating that at this point the stream is normal to continue readable if it is IO. EOF means the data is at the end.

Client-side Streaming RPC

// Create a random number of random pointsr := rand.New(rand.NewSource(time.Now().UnixNano()))pointCount := int(r.Int31n(100)) + 2 // Traverse at least two pointsvar points []*pb.Pointfor i := 0; i < pointCount; i++ {    points = append(points, randomPoint(r))}log.Printf("Traversing %d points.", len(points))stream, err := client.RecordRoute(context.Background())if err != nil {    log.Fatalf("%v.RecordRoute(_) = _, %v", client, err)}for _, point := range points {    if err := stream.Send(point); err != nil {        log.Fatalf("%v.Send(%v) = %v", stream, point, err)    }}reply, err := stream.CloseAndRecv()if err != nil {    log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)}log.Printf("Route summary: %v", reply)

See also the definition of Recordroute in Route_guide.pb.go

func (c *routeGuideClient) RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error) {    stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[1], c.cc, "/routeguide.RouteGuide/RecordRoute", opts...)

The Recordroute method simply passes a context parameter and then returns a Routeguide_recordrouteclient stream object for the client to write the message and read the message.

The Send () method of the routeguide_recordrouteclient is used to send a request to the client, and once all the client's requests have been completed, the client needs to call the Closeandrecv method to let Grpc know that the client has completed the request and expects to get a response.
If the err returned by CLOSEANDRECV () is not nil, then the first value returned is a valid service-side response.

Bidirectional streaming RPC

stream, err := client.RouteChat(context.Background())waitc := make(chan struct{})go func() {    for {        in, err := stream.Recv()        if err == io.EOF {            // read done.            close(waitc)            return        }        if err != nil {            log.Fatalf("Failed to receive a note : %v", err)        }        log.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)    }}()for _, note := range notes {    if err := stream.Send(note); err != nil {        log.Fatalf("Failed to send a note: %v", err)    }}stream.CloseSend()<-waitc

And the Recordroute type, method routechat only needs to pass a context object, returning a routeguide_routechatclient for the client to read the message and write the message.

func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) {    stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[2], c.cc, "/routeguide.RouteGuide/RouteChat", opts...)

However, unlike Recordroute, the client writes messages to the stream in the client, and the server also writes messages to the stream on the server. In addition, the method in the client read and write is separate run independently, there is no sequencing, there is the client after writing the message is finished using Closesend instead of CLOSEANDRECV

Postscript

has been in the csdn to write articles, the latter will be gradually converted to the book, but also ask you to support a lot.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.