定义服务
.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 main
import (
"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()
//注册到grpc
pb.RegisterHelloServer(s, &server{})
err = s.Serve(list)
if err != nil {
log.Fatalln(err)
}
}
主要内容:
- 实现在hello.proto定义的服务接口HelloServer
- 搭建grpc的服务器,用来监听来自客户端的请求和响应客户端
客户端
package main
import (
"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 futures
import time
import grpc
import gpio.hello_pb2 as hello_pb2
import gpio.hello_pb2_grpc as hello_pb2_grpc
# 实现 proto 文件中定义的 GreeterServicer
class 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 seconds
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
客户端
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def 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()