定义服务
.prote官方实例
syntax = "proto3";option java_multiple_files = true;option java_package = "io.grpc.examples.routeguide";option java_outer_classname = "RouteGuideProto";package routeguide;// Interface exported by the server.service RouteGuide {// A simple RPC.//// Obtains the feature at a given position.//// A feature with an empty name is returned if there's no feature at the given// position.rpc GetFeature(Point) returns (Feature) {}// A server-to-client streaming RPC.//// 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) {}// A client-to-server streaming RPC.//// Accepts a stream of Points on a route being traversed, returning a// RouteSummary when traversal is completed.rpc RecordRoute(stream Point) returns (RouteSummary) {}// A Bidirectional streaming RPC.//// 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) {}}// 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;}// A latitude-longitude rectangle, represented as two diagonally opposite// points "lo" and "hi".message Rectangle {// One corner of the rectangle.Point lo = 1;// The other corner of the rectangle.Point hi = 2;}// A feature names something at a given point.//// If a feature could not be named, the name is empty.message Feature {// The name of the feature.string name = 1;// The point where the feature is detected.Point location = 2;}// A RouteNote is a message sent while at a given point.message RouteNote {// The location from which the message is sent.Point location = 1;// The message to be sent.string message = 2;}// A RouteSummary is received in response to a RecordRoute rpc.//// It contains the number of individual points received, the number of// detected features, and the total distance covered as the cumulative sum of// the distance between each point.message RouteSummary {// The number of points received.int32 point_count = 1;// The number of known features passed while traversing the route.int32 feature_count = 2;// The distance covered in metres.int32 distance = 3;// The duration of the traversal in seconds.int32 elapsed_time = 4;}
在.proto文件中指定一个具名的service
service RouteGuide {...}
- 一个简单的RPC,客户端使用存根将请求发送到服务器,然后等待响应返回,就像普通的函数调用一样。
// 获得给定位置的特征rpc GetFeature(Point) returns (Feature) {}
- 服务器端流式RPC,客户端向服务器发送请求,并获取流以读取回一系列消息。客户端从返回的流中读取,直到没有更多消息为止。如我们的示例所示,可以通过将stream关键字放在响应类型之前来指定服务器端流方法。
//获得给定Rectangle中可用的特征。结果是//流式传输而不是立即返回//因为矩形可能会覆盖较大的区域并包含大量特征。rpc ListFeatures(Rectangle) returns (stream Feature) {}
- 客户端流式RPC,其中客户端使用gRPC提供的流写入一系列消息并将其发送到服务器。客户端写完消息后,它将等待服务器读取所有消息并返回其响应。通过将stream关键字放在请求类型之前,可以指定客户端流方法。
// 接收路线上被穿过的一系列点位, 当行程结束时// 服务端会返回一个RouteSummary类型的消息.rpc RecordRoute(stream Point) returns (RouteSummary) {}
- 双向流式RPC,双方都使用读写流发送一系列消息。这两个流是独立运行的,因此客户端和服务器可以按照自己喜欢的顺序进行读写:例如,服务器可以在写响应之前等待接收所有客户端消息,或者可以先读取消息再写入消息,或其他一些读写组合。每个流中的消息顺序都会保留。您可以通过在请求和响应之前都放置stream关键字来指定这种类型的方法。
//接收路线行进中发送过来的一系列RouteNotes类型的消息,同时也接收其他RouteNotes(例如:来自其他用户)rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
生成客户端和服务端代码
在终端键入protoc -I . —go_out=plugins=grpc:. ./hello.proto
- -I:指定对导入文件的搜索路径
- —go_out=:指定生成文件的路径,这里指定了生成在当前路径
- plugins=grpc: 用grpc plugin来执行这段protoc command
- ./hello.proto: 指定待编译的 proto文件
实例
.proto文件
syntax = "proto3";//服务接口service Hello {rpc Hello(Request) returns (Response){};}message Request {string name = 1;}message Response {string message = 1;}
执行: protoc -I proto —go_out=plugins=grpc:proto ./proto/hello.proto
服务端
package mainimport ("context""log""net"pb "test/proto""google.golang.org/grpc")type server struct {}//实现HelloServer接口,用于被调用func (s *server) Hello(ctx context.Context,in *pb.Request) (*pb.Response, error){log.Println("Receive:", in.Name)return &pb.Response{Message: "Hello!" + in.Name}, nil}func main() {list, err := net.Listen("tcp", ":9090")if err != nil {log.Fatalln(err)}s := grpc.NewServer()//注册到grpcpb.RegisterHelloServer(s, &server{})err = s.Serve(list)if err != nil {log.Fatalln(err)}}
主要内容:
- 实现在hello.proto定义的服务接口HelloServer
- 搭建grpc的服务器,用来监听来自客户端的请求和响应客户端
客户端
package mainimport ("context""google.golang.org/grpc""log""os"pb "test/proto""time")func main() {//连接服务端句柄//WithInsecure()不指定安全选项,安全选项是一种授权认证(如, TLS,GCE认证,JWT认证)conn, err := grpc.Dial("localhost:9090", grpc.WithInsecure())if err != nil {log.Fatalln(err)}defer conn.Close()cli := pb.NewHelloClient(conn)//设置上下文超时ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()//响应name := "liz"resp, err := cli.Hello(ctx, &pb.Request{Name:name})if err != nil {log.Fatal(err)}log.Println(resp.Message)}
python使用grpc
pip install grpcio
安装 python 下的 protoc 编译器
pip install grpcio-tools编译 proto 文件
python -m grpc_tools.protoc —python_out=. —grpc_python_out=. -I. helloworld.proto
- python -m grpc_tools.protoc: python 下的 protoc 编译器通过 python 模块(module) 实现
- python_out=. : 编译生成处理 protobuf 相关的代码的路径, 这里生成到当前目录
- grpc_python_out=. : 编译生成处理 grpc 相关的代码的路径, 这里生成到当前目录
- -I. helloworld.proto : proto 文件的路径, 这里的 proto 文件在当前目录
编译后生成的代码:
- helloworld_pb2.py: 用来和 protobuf 数据进行交互
- helloworld_pb2_grpc.py: 用来和 grpc 进行交互
服务端
from concurrent import futuresimport timeimport grpcimport gpio.hello_pb2 as hello_pb2import gpio.hello_pb2_grpc as hello_pb2_grpc# 实现 proto 文件中定义的 GreeterServicerclass Greeter(hello_pb2_grpc.HelloServicer):# 实现 proto 文件中定义的 rpc 调用def Hello(self, request, context):return hello_pb2.Response(message = 'hello {msg}'.format(msg = request.name))def serve():# 启动 rpc 服务server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))hello_pb2_grpc.add_HelloServicer_to_server(Greeter(), server)server.add_insecure_port('[::]:9090')server.start()try:while True:time.sleep(60*60*24) # one day in secondsexcept KeyboardInterrupt:server.stop(0)if __name__ == '__main__':serve()
客户端
import grpcimport helloworld_pb2import helloworld_pb2_grpcdef run():# 连接 rpc 服务器channel = grpc.insecure_channel('localhost:50051')# 调用 rpc 服务stub = helloworld_pb2_grpc.GreeterStub(channel)response = stub.SayHello(helloworld_pb2.HelloRequest(name='czl'))print("Greeter client received: " + response.message)response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='daydaygo'))print("Greeter client received: " + response.message)if __name__ == '__main__':run()
