定义服务

.prote官方实例

  1. syntax = "proto3";
  2. option java_multiple_files = true;
  3. option java_package = "io.grpc.examples.routeguide";
  4. option java_outer_classname = "RouteGuideProto";
  5. package routeguide;
  6. // Interface exported by the server.
  7. service RouteGuide {
  8. // A simple RPC.
  9. //
  10. // Obtains the feature at a given position.
  11. //
  12. // A feature with an empty name is returned if there's no feature at the given
  13. // position.
  14. rpc GetFeature(Point) returns (Feature) {}
  15. // A server-to-client streaming RPC.
  16. //
  17. // Obtains the Features available within the given Rectangle. Results are
  18. // streamed rather than returned at once (e.g. in a response message with a
  19. // repeated field), as the rectangle may cover a large area and contain a
  20. // huge number of features.
  21. rpc ListFeatures(Rectangle) returns (stream Feature) {}
  22. // A client-to-server streaming RPC.
  23. //
  24. // Accepts a stream of Points on a route being traversed, returning a
  25. // RouteSummary when traversal is completed.
  26. rpc RecordRoute(stream Point) returns (RouteSummary) {}
  27. // A Bidirectional streaming RPC.
  28. //
  29. // Accepts a stream of RouteNotes sent while a route is being traversed,
  30. // while receiving other RouteNotes (e.g. from other users).
  31. rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
  32. }
  33. // Points are represented as latitude-longitude pairs in the E7 representation
  34. // (degrees multiplied by 10**7 and rounded to the nearest integer).
  35. // Latitudes should be in the range +/- 90 degrees and longitude should be in
  36. // the range +/- 180 degrees (inclusive).
  37. message Point {
  38. int32 latitude = 1;
  39. int32 longitude = 2;
  40. }
  41. // A latitude-longitude rectangle, represented as two diagonally opposite
  42. // points "lo" and "hi".
  43. message Rectangle {
  44. // One corner of the rectangle.
  45. Point lo = 1;
  46. // The other corner of the rectangle.
  47. Point hi = 2;
  48. }
  49. // A feature names something at a given point.
  50. //
  51. // If a feature could not be named, the name is empty.
  52. message Feature {
  53. // The name of the feature.
  54. string name = 1;
  55. // The point where the feature is detected.
  56. Point location = 2;
  57. }
  58. // A RouteNote is a message sent while at a given point.
  59. message RouteNote {
  60. // The location from which the message is sent.
  61. Point location = 1;
  62. // The message to be sent.
  63. string message = 2;
  64. }
  65. // A RouteSummary is received in response to a RecordRoute rpc.
  66. //
  67. // It contains the number of individual points received, the number of
  68. // detected features, and the total distance covered as the cumulative sum of
  69. // the distance between each point.
  70. message RouteSummary {
  71. // The number of points received.
  72. int32 point_count = 1;
  73. // The number of known features passed while traversing the route.
  74. int32 feature_count = 2;
  75. // The distance covered in metres.
  76. int32 distance = 3;
  77. // The duration of the traversal in seconds.
  78. int32 elapsed_time = 4;
  79. }

在.proto文件中指定一个具名的service

  1. service RouteGuide {
  2. ...
  3. }
  • 一个简单的RPC,客户端使用存根将请求发送到服务器,然后等待响应返回,就像普通的函数调用一样。
  1. // 获得给定位置的特征
  2. rpc GetFeature(Point) returns (Feature) {}
  • 服务器端流式RPC,客户端向服务器发送请求,并获取流以读取回一系列消息。客户端从返回的流中读取,直到没有更多消息为止。如我们的示例所示,可以通过将stream关键字放在响应类型之前来指定服务器端流方法。
  1. //获得给定Rectangle中可用的特征。结果是
  2. //流式传输而不是立即返回
  3. //因为矩形可能会覆盖较大的区域并包含大量特征。
  4. rpc ListFeatures(Rectangle) returns (stream Feature) {}
  • 客户端流式RPC,其中客户端使用gRPC提供的流写入一系列消息并将其发送到服务器。客户端写完消息后,它将等待服务器读取所有消息并返回其响应。通过将stream关键字放在请求类型之前,可以指定客户端流方法。
  1. // 接收路线上被穿过的一系列点位, 当行程结束时
  2. // 服务端会返回一个RouteSummary类型的消息.
  3. rpc RecordRoute(stream Point) returns (RouteSummary) {}
  • 双向流式RPC,双方都使用读写流发送一系列消息。这两个流是独立运行的,因此客户端和服务器可以按照自己喜欢的顺序进行读写:例如,服务器可以在写响应之前等待接收所有客户端消息,或者可以先读取消息再写入消息,或其他一些读写组合。每个流中的消息顺序都会保留。您可以通过在请求和响应之前都放置stream关键字来指定这种类型的方法。
  1. //接收路线行进中发送过来的一系列RouteNotes类型的消息,同时也接收其他RouteNotes(例如:来自其他用户)
  2. rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

生成客户端和服务端代码

在终端键入protoc -I . —go_out=plugins=grpc:. ./hello.proto
GRPC - 图1

  • -I:指定对导入文件的搜索路径
  • —go_out=:指定生成文件的路径,这里指定了生成在当前路径
  • plugins=grpc: 用grpc plugin来执行这段protoc command
  • ./hello.proto: 指定待编译的 proto文件

实例

.proto文件

  1. syntax = "proto3";
  2. //服务接口
  3. service Hello {
  4. rpc Hello(Request) returns (Response){};
  5. }
  6. message Request {
  7. string name = 1;
  8. }
  9. message Response {
  10. string message = 1;
  11. }

执行: protoc -I proto —go_out=plugins=grpc:proto ./proto/hello.proto

服务端

  1. package main
  2. import (
  3. "context"
  4. "log"
  5. "net"
  6. pb "test/proto"
  7. "google.golang.org/grpc"
  8. )
  9. type server struct {}
  10. //实现HelloServer接口,用于被调用
  11. func (s *server) Hello(ctx context.Context,in *pb.Request) (*pb.Response, error){
  12. log.Println("Receive:", in.Name)
  13. return &pb.Response{Message: "Hello!" + in.Name}, nil
  14. }
  15. func main() {
  16. list, err := net.Listen("tcp", ":9090")
  17. if err != nil {
  18. log.Fatalln(err)
  19. }
  20. s := grpc.NewServer()
  21. //注册到grpc
  22. pb.RegisterHelloServer(s, &server{})
  23. err = s.Serve(list)
  24. if err != nil {
  25. log.Fatalln(err)
  26. }
  27. }

主要内容:

  • 实现在hello.proto定义的服务接口HelloServer
  • 搭建grpc的服务器,用来监听来自客户端的请求和响应客户端

客户端

  1. package main
  2. import (
  3. "context"
  4. "google.golang.org/grpc"
  5. "log"
  6. "os"
  7. pb "test/proto"
  8. "time"
  9. )
  10. func main() {
  11. //连接服务端句柄
  12. //WithInsecure()不指定安全选项,安全选项是一种授权认证(如, TLS,GCE认证,JWT认证)
  13. conn, err := grpc.Dial("localhost:9090", grpc.WithInsecure())
  14. if err != nil {
  15. log.Fatalln(err)
  16. }
  17. defer conn.Close()
  18. cli := pb.NewHelloClient(conn)
  19. //设置上下文超时
  20. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  21. defer cancel()
  22. //响应
  23. name := "liz"
  24. resp, err := cli.Hello(ctx, &pb.Request{Name:name})
  25. if err != nil {
  26. log.Fatal(err)
  27. }
  28. log.Println(resp.Message)
  29. }

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 进行交互

服务端

  1. from concurrent import futures
  2. import time
  3. import grpc
  4. import gpio.hello_pb2 as hello_pb2
  5. import gpio.hello_pb2_grpc as hello_pb2_grpc
  6. # 实现 proto 文件中定义的 GreeterServicer
  7. class Greeter(hello_pb2_grpc.HelloServicer):
  8. # 实现 proto 文件中定义的 rpc 调用
  9. def Hello(self, request, context):
  10. return hello_pb2.Response(message = 'hello {msg}'.format(msg = request.name))
  11. def serve():
  12. # 启动 rpc 服务
  13. server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  14. hello_pb2_grpc.add_HelloServicer_to_server(Greeter(), server)
  15. server.add_insecure_port('[::]:9090')
  16. server.start()
  17. try:
  18. while True:
  19. time.sleep(60*60*24) # one day in seconds
  20. except KeyboardInterrupt:
  21. server.stop(0)
  22. if __name__ == '__main__':
  23. serve()

客户端

  1. import grpc
  2. import helloworld_pb2
  3. import helloworld_pb2_grpc
  4. def run():
  5. # 连接 rpc 服务器
  6. channel = grpc.insecure_channel('localhost:50051')
  7. # 调用 rpc 服务
  8. stub = helloworld_pb2_grpc.GreeterStub(channel)
  9. response = stub.SayHello(helloworld_pb2.HelloRequest(name='czl'))
  10. print("Greeter client received: " + response.message)
  11. response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='daydaygo'))
  12. print("Greeter client received: " + response.message)
  13. if __name__ == '__main__':
  14. run()

参考

gRPC教程 - 李文周的博客