In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly introduces how to use Golang to build gRPC services, the article is very detailed, has a certain reference value, interested friends must read it!
Why use gRPC
Our example is a simple roadmap application where clients can obtain route feature information, create their route summaries, and exchange route information such as traffic status updates with servers or other clients.
With gRPC, we can define our services in .proto files and implement clients and servers in any language supported by gRPC, which in turn can run in environments ranging from servers to your own tablet-gRPC will also solve the complexity of communication between all different languages and environments. We also get all the benefits of using protocol buffer, including efficient serialization (faster and more efficient than JSON in both speed and size), simple IDL (Interface definition language), and easy interface updates.
Install the GPC package
First, you need to install the gRPC golang version of the package, and the examples directory of the official package contains the code for the sample roadmap application in the tutorial.
$go get google.golang.org/grpc
Then change to grpc-go/examples/route_guide: directory:
$cd $GOPATH/src/google.golang.org/grpc/examples/route_guide installs related tools and plug-ins
The easiest way to install the protocol buffer compiler is to go to https://github.com/protocolbuffers/protobuf/releases to download the precompiled protoc binaries, which can be found in the repository for each platform. Here we take Mac Os as an example to download and extract the file from https://github.com/protocolbuffers/protobuf/releases/download/v3.6.0/protoc-3.6.0-osx-x86_64.zip. Update the PATH system variable, or make sure that protoc is placed in the directory contained in PATH.
Install the protoc compiler plug-in
$go get-u github.com/golang/protobuf/protoc-gen-go
The compiler plug-in protoc-gen-go will be installed in $GOBIN, which defaults to $GOPATH/bin. The compiler protoc must find it in $PATH:
$export PATH=$PATH:$GOPATH/bin definition service
The first step is to use protocol buffer to define the request and response types for gRPC services and methods. You can see the complete .proto file in the downloaded sample code examples/route_guide/routeguide/route_guide.proto.
To define a service, you need to specify a named service in the .proto file
Service RouteGuide {...}
Then define the rpc methods in the service definition, specifying their request and response types. GRPC allows you to define four types of service methods, all of which will be applied to our RouteGuide service.
A simple RPC where the client uses a stub to send the request to the server and then waits for the response to return, just like a normal function call.
/ / get the feature rpc GetFeature (Point) returns (Feature) {} of a given location
The server stream RPC, and the client sends a request to the server and gets the stream to read back a series of messages. The client reads from the returned stream until there are no more messages. As our example shows, you can specify a server-side flow method by placing the stream keyword before the response type.
/ / get the features available in a given Rectangle. The result is / / streaming rather than immediate return / / because the rectangle may cover a large area and contain a large number of features. Rpc ListFeatures (Rectangle) returns (stream Feature) {}
The client stream RPC, where the client writes a series of messages using the stream provided by gRPC and sends them to the server. After the client has finished writing the message, it waits for the server to read all the messages and return its response. You can specify a client flow method by placing the stream keyword before the request type.
/ / A series of points passed through on the receiving route. / / the server will return a message of type RouteSummary at the end of the journey. RPC RecordRoute (stream Point) returns (RouteSummary) {}
Bi-directional streaming RPC, where both parties use read-write streams to send a series of messages. The two streams run independently, so the client and server can read and write in the order they like: for example, the server can wait to receive all client messages before writing the response, or it can read the message before writing it, or some other combination of read and write. The order of messages in each flow is preserved. You can specify this type of method by placing the stream keyword before both the request and response.
/ / receive a series of RouteNotes type messages sent during routing, and also receive other RouteNotes (for example, from other users) rpc RouteChat (stream RouteNote) returns (stream RouteNote) {}
We also need protocol buffer message type definitions for all request and response types in our .proto file. For example, the following Point message types:
/ / Points is expressed as a longitude-latitude pair in the E7 representation. / / (degrees multiplied by 10 * * 7 and rounded to the nearest integer). / / Latitude should be in the range of + /-90 degrees, and longitude should be in the range of + /-180 degrees (inclusive) message Point {int32 latitude = 1; int32 longitude = 2;} generate client and server code
The next step is to generate the interface between the gRPC client and the server from our .proto service definition. We use the protoc compiler and the compiler plug-in installed above to do this:
Run under the directory of the sample route_guide:
Protoc-I routeguide/ routeguide/route_guide.proto-- go_out=plugins=grpc:routeguide
After running the command, the route_guide.pb.go file is generated in the routeguide directory of the sample route_guide directory.
The pb.go file contains:
All the protocol buffer code used to populate, serialize, and retrieve the request and response message types we defined.
A client stub is used to let the client invoke the methods defined in the RouteGuide service.
An interface type RouteGuideServer that needs to be implemented on the server side, which contains all the methods defined in the RouteGuide service.
Create a gRPC server
First let's take a look at how to create a RouteGuide server. There are two ways to make our RouteGuide service work:
Implement the service interface we generate from the service definition: do what the service actually does.
Run a gRPC server to listen to the client's request and dispatch the request to the correct service implementation. You can find the implementation code of the RouteGuide` service in our example in the grpc-go/examples/route_guide/server/server.go of the gPRC package you just installed. Let's see how he works.
Implement RouteGuide
As you can see, there is a routeGuideServer structure type in the implementation code that implements the RouteGuideServer interface defined in the pb.go file generated by the protoc compiler.
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 {...}. Ordinary PRC
RouteGuideServer implements all of our service methods. First, let's look at the simplest type, GetFeature, which simply takes a Point from the client and returns the corresponding Feature information from its Feature 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 passes the RPC context object and the client's Point protocol buffer request message, which returns a protocol buffer message and error of type Feature in the response message. In this method, we populate the Feature with the appropriate information, then return it with a nil error to tell gRPC that we have finished processing the RPC and that we can return the Feature to the client.
Server-end streaming RPC
Now, let's look at a streaming RPC in the service method. ListFeatures is a server-side streaming RPC, so we need to send multiple Feature back 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, instead of getting a simple request and response object this time, we get a request object (where the client looks for the Rectangle of Feature) and a special RouteGuide_ListFeaturesServer object to write the response.
In this method, we populate all the Feature objects that need to be returned and write them to RouteGuide_ListFeaturesServer using the Send () method. Finally, as in a simple RPC, we return a nil error to tell gRPC that we have finished writing the response. If any errors occur in this call, we will return a non-nil error; the gRPC layer will transition it to the appropriate RPC state to send online.
Client streaming RPC
Now, let's look at something more complicated: the client flow method RecordRoute, which takes the point stream from the client and returns a RouteSummary that contains the travel information. As you can see, this time the method has no request parameter at all. Instead, it gets a RouteGuide_RecordRouteServer stream that the server can use to read and write messages-it can receive client messages using the Recv () method and return its single response using the SendAndClose () method.
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 the method body, we use the Recv () method of RouteGuide_RecordRouteServer to constantly read the client's request into a request object (in this case, Point) until there are no more messages: the server needs to check for errors returned from Recv () after each call. If nil, the flow is still good and can continue reading; if it is io.EOF, the message flow is over and the server can return its RouteSummary. If the error is another value, we will return the error "as is" so that the gRPC layer can transition it to the RPC state.
Bi-directional flow RPC
Finally, let's take a look at the bi-directional flow RPC method RouteChat ()
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) s.mu.Lock () s.routeNotes [key] = append (s.routeNotes [key] In) / / Note: this copy prevents blocking other clients while serving this one. / / We don't need to do a deep copy, because elements in the slice are / / insert-only and never modified. Rn: = make ([] * pb.RouteNote, len (s.routeNotes [key]) copy (rn, s.routeNotes [key]) s.mu.Unlock () for _, note: = range rn {if err: = stream.Send (note); err! = nil {return err}
This time, we get a RouteGuide_RouteChatServer stream, which, as in the client flow example, can be used to read and write messages. This time, however, when the client is still writing messages to its message flow, we will write the message to be returned to the flow.
The read and write syntax here is very similar to our client-side streaming method, except that the server uses the Send () method of the stream instead of SendAndClose (), because the server writes multiple responses. Although both parties always get each other's messages in the order in which they are written, both the client and the server can read and write in any order-the stream runs completely independently (meaning that the server can accept the request and then write the stream. You can also receive a request and write a response. The same client can either write the request and then read the response, or send a request to read a response)
Start the server
Once we have implemented all the methods, we also need to start the gRPC server so that the client can actually use our service. The following code snippet shows how to start the RouteGuide service.
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)
In order to build and start the server, we need:
Specify the interface lis to listen for client requests, err: = net.Listen ("tcp", fmt.Sprintf (":% d", * port)).
Create an instance of gRPC server using grpc.NewServer ().
Register our service implementation with gRPC server.
Use our port details to call Serve () on the server to block and wait until the process is killed or Stop () is called.
Create a client
In this section we will create a Go client for the RouteGuide service, and you can see the complete client code in grpc-go/examples/route_guide/client/client.go.
Create a client stub
To invoke the method of the service, we first need to create a gRPC channel to communicate with the server. We create a channel by passing the server address and port number to grpc.Dial (), like this:
Conn, err: = grpc.Dial (* serverAddr) if err! = nil {...} defer conn.Close ()
If the service you request requires authentication, you can use DialOptions in grpc.Dial to set authentication credentials (such as TLS,GCE credentials, JWT credentials)-but our RouteGuide service does not need these.
After setting up the gRPC channel, we need a client stub to execute RPC. We use the NewRouteGuideClient method provided in the pb package generated from .proto to obtain the client stub.
Client: = pb.NewRouteGuideClient (conn)
The generated pb.go file defines the client interface type RouteGuideClient and implements the methods in the interface with the structure type of the client stub, so the client stub client obtained above can directly call the methods listed in the following interface types.
Type RouteGuideClient interface {GetFeature (ctx context.Context, in * Point, opts... grpc.CallOption) (* Feature, error) ListFeatures (ctx context.Context, in * Rectangle, opts... grpc.CallOption) (RouteGuide_ListFeaturesClient, error) RecordRoute (ctx context.Context, opts... grpc.CallOption) (RouteGuide_RecordRouteClient, error) RouteChat (ctx context.Context, opts... grpc.CallOption) (RouteGuide_RouteChatClient, error)}
Each implementation method will then request the corresponding method of the gRPC server to get the response of the server, such as:
Func (c * routeGuideClient) GetFeature (ctx context.Context, in * Point, opts... grpc.CallOption) (* Feature, error) {out: = new (Feature) err: = c.cc.Invoke (ctx, "/ routeguide.RouteGuide/GetFeature", in, out, opts...) If err! = nil {return nil, err} return out, nil}
The complete implementation of the RouteGuideClient interface can be found in the generated pb.go file.
The method of invoking the service
Now let's look at the method of how to invoke the service. Note that in gRPC-Go, PRC runs in blocking / synchronous mode, which means that the RPC call waits for a response from the server, which returns a response or an error.
Ordinary RPC
Calling the normal RPC method GetFeature is like calling a local method directly.
Feature, err: = client.GetFeature (context.Background (), & pb.Point {409146138,-746188906}) if err! = nil {...}
As you can see, we call this method on the stub we obtained earlier. In our method parameters, we create and populate a protocol buffer object (in this case, a Point object). We also pass a context.Context object that allows us to change the behavior of the RPC if necessary, such as timeout / cancel the RPC (cancel an RPC in flight) that is being called. If the call does not return an error, we can read the server's response information from the first return value.
Server-end streaming RPC
Here we will call the server end-flow method ListFeatures, and the stream returned by the method contains geographic feature information. If you've read the above section on creating a client, there's something here that looks familiar-streaming RPC is implemented in a similar way on both sides.
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)}
As with a simple RPC call, the call passes a method's context and a request. But what we get back is an instance of RouteGuide_ListFeaturesClient instead of a response object. The client can use the RouteGuide_ListFeaturesClient stream to read the server's response.
We use the Recv () method of RouteGuide_ListFeaturesClient to keep reading the server's response into a protocol buffer response object (the Feature object in this case) until there are no more messages: the client needs to check for the error err returned from Recv () after each call. If nil, the flow is still good and can continue to read; if it is io.EOF, the message flow has ended; otherwise, it is a certain RPC error, which is passed to the caller through err.
Client streaming RPC
The client-side flow method RecordRoute is similar to the server-side method, except that we pass only a context to the method and get a RouteGuide_RecordRouteClient stream that can be used to write and read messages.
/ / randomly create some 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 ITunes + {points = append (points, randomPoint (r))} log.Printf ("Traversing% d points.", len (points)) stream, err: = client.RecordRoute (context.Background ()) / / call client streaming RPC method if err! = nil {log.Fatalf ("% v.RecordRoute (_) = _,% v", client, err)} for _, point: = range points {if err: = stream.Send (point) Err! = nil {/ / write multiple request messages if err = = io.EOF {break} log.Fatalf ("% v.Send (% v) =% v", stream, point, err)}} reply, err: = stream.CloseAndRecv () / / retrieve server response if err! = nil {log.Fatalf ("% v.CloseAndRecv () got error% v, want% v", stream, err) Nil)} log.Printf ("Route summary:% v", reply)
RouteGuide_RecordRouteClient has a Send (). We can use it to send requests to the server. Once we have finished writing to the stream using Send (), we need to call the CloseAndRecv () method on the stream to let gRPC know that we have finished writing the request and expect a response. We can get the RPC status from the err returned by the CloseAndRecv () method. If the status is the first return value of nil,CloseAndRecv (), it is a valid server response.
Bi-directional flow RPC
Finally, let's take a look at bi-directional streaming RPC RouteChat (). As with RecordRoute, we only pass a context object to the method and then get a stream that can be used to write and read messages. This time, however, we return the value through the flow of the method while the server still writes the message to the message flow.
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 ()
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.