简介
- gRPC 可以将 Protocol buffers 用作其接口定义语言 ( IDL ) 和底层消息交换格式(也可以使用其他的,例如json)
- 远程调用,跨语言,更容易创建分布式应用和服务
- 和其他RPC一样,基于服务定义的思想,结合Protocol buffers gRPC 插件,定义好服务后,服务端实现相应接口,客户端直接调用生成好的方法即可
主要使用场景
- 低延迟、高度可扩展的分布式系统。
- 开发与云服务器通信的移动客户端。
- 设计一个需要准确、高效和语言独立的新协议。
- 分层设计以实现扩展,例如。身份验证、负载平衡、日志记录和监控等。
核心概念
定义服务
代码语言:javascript复制service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
四种方式
- 简单rpc,就像正常调用函数一样
rpc GetFeature(Point) returns (Feature) {}
流式rpc
解决的问题
- 传输的数据太大,造成瞬时网络压力
- 大数据需要接收完才能处理
分类
- 服务端流式rpc
rpc ListFeatures(Rectangle) returns (stream Feature) {}
- 客户端流式rpc
rpc RecordRoute(stream Point) returns (RouteSummary) {}
- 双向流式rpc
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
- 服务端收到请求后,不用等到读取完才响应,可以接收一点,响应一点,官网的原话是:服务器和客户端可以玩“乒乓”
具体介绍看官方文档,现在没用到这块
同步和异步
在 gRPC-Go 中,RPC 以阻塞/同步模式运行,这意味着 RPC 调用等待服务器响应,并且将返回响应或错误。
看来只能自己起goroutine了,但这样很麻烦,野生goroutine需要自己去维护异常处理和日志
设置超时
可主动终止RPC
示例
定义服务helloworld.proto
代码语言:javascript复制syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
利用buf生成代码
执行buf mod init生成buf.yaml
配置buf,buf.gen.yaml
代码语言:javascript复制# 配置protoc生成规则
version: v1
managed:
enabled: true
go_package_prefix:
# proto文件中不使用option定义包名称,因为proto生成代码可以放在不同项目中使用,因此在buf.gen.yaml中进行定义
default: helloworld
except:
- buf.build/googleapis/googleapis
plugins:
# 使用go插件生成go代码
- name: go
out: ./
opt: paths=source_relative
# 使用go-grpc插件生成grpc代码
- name: go-grpc
out: ./
opt:
- paths=source_relative
- require_unimplemented_servers=false
执行buf generate 生成helloworld.pb.go
、helloworld_grpc.pb.go
创建服务端
代码语言:javascript复制type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " in.GetName()}, nil
}
- 继承生成的默认实现类
- 重写实现接口方法
启动服务端
代码语言:javascript复制 flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
- 监听端口
- 创建 gRPC 服务器的实例
- 向 gRPC 服务器注册我们的服务实现
- 调用
Serve()
服务器以进行阻塞等待,直到进程被杀死或被Stop()
调用
创建客户端
代码语言:javascript复制 flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
- 创建 gRPC通道来与服务器通信
- 可以使用
DialOptions
在服务需要时设置身份验证凭据(例如,TLS、GCE 凭据或 JWT 凭据) - 创建客户端
- 调用服务方法