基本概念
什么是 gRPC?
gRPC
最初是 google 开发的高性能,且功能强大的开源 RPC 框架,后来被纳入云原生基金会的托管项目中,由于背靠 google 老大哥,不论是技术储备还是生态建设都十分的成熟和完善。是一个应用非常广泛的 RPC
框架。
由RPC 详解我们知道,RPC 框架中有两个核心组件,客户端存根(client-stub)和服务端存根(server-stub)。业务代码通过调用存根提供的接口,使得远程调用看起来像本地接口调用一样。要创建这两个存根,我们需要明确客户端存根和和服务端存根传递消息的格式,传递消息使用的协议,以及类似参数传递等问题(这些在RPC 详解中已经介绍,这里不再赘述)。而定义这些服务使用的语言我们称作(IDL),服务定义语言(interface definition language)。
gRPC
使用 proto buffers
作为服务定义语言,编写 proto 文件,即可完成服务的定义。
gRPC 为什么使用 Proto Buffers?
- 可读性好
- 支持多种语言代码的生成
- 二进制数据表示,传输更快,序列化和反序列化更高效;
- 采用强类型协议,更稳定
- 兼容性更好,新增接口服务不影响原服务的使用
如何定义一个 protocol message
syntax = "proto3";
message <NameOfTheMessage> {
<data-type> name_of_field_1 = tag_1;
<data-type> name_of_field_2 = tag_2;
...
<data-type> name_of_field_n = tag_n;
}
- 消息类型采用驼峰格式
- 消息域采用小写字母蛇形格式
- 基本数据类型有:
string, bool, bytes,
float, double
int32, int64, uint32, uint4, sint32, sint64, etc. - 数据类型也可以是自定义的消息类型和枚举类型
- tag_n :字段编号,用于二进制消息中识别各个字段,该编号在每个消息中唯一,且不可修改。注意在将message编码成二进制消息体时字段编号1-15将会占用1个字节,16-2047将占用两个字节。所以在一些频繁使用用的message中,你应该总是先使用前面1-15字段编号。
- 注意在将message编码成二进制消息体时字段编号1-15将会占用1个字节,16-2047将占用两个字节。所以在一些频繁使用用的message中,你应该总是先使用前面1-15字段编号。
如何使用 proto buffers 定义一个 gRPC 服务?
mysql.proto 定义了一个 SelectRecord
gRPC 接口
syntax = "proto3"; // 1
package sample; // 2
option go_package = "./;pb"; // 3
import "google/api/annotations.proto"; // 4
message CreateMysqlRequest { // 5
string sql = 1; // 6
}
message CreateMysqlResponse { // 7
string body = 1; // 8
}
service MysqlService { // 9
rpc SelectRecord(CreateMysqlRequest) returns (CreateMysqlResponse) { // 10
option (google.api.http) = { // 11
post : "/v1/mysql/select"
body : "*"
};
};
}
syntax = "proto3";
,文件开头指定版本号;package sample;
,定义本服务的包名,避免不同服务相同消息类型产生冲突;option go_package = "./;pb";
,生成 go 代码对应的路径和目录;import "google/api/annotations.proto";
,需要引用外部 proto 文件时使用;CreateMysqlRequest
,请求消息类型定义;sql
,定义消息中域的名称和类型,支持的类型可参考下面的链接,后面的数字同一个类型下面必须唯一;CreateMysqlResponse
,响应消息类型;body
,定义消息中域的名称和类型,支持的类型可参考下面的链接,后面的数字同一个类型下面必须唯一;MysqlService
,定义服务的接口名称;SelectRecord
,远程调用方法名;option (google.api.http)
,gRPC
网关,用于支持 http 协议。
关于 proto buffers
详细内容可以参考谷歌的教程:https://developers.google.com/protocol-buffers/docs/gotutorial
使用编译器 protoc
,可以将 proto 文件编译生成客户端和服务端存根代码,要让这些代码能能够正常工作,我们还需要做以下事情。
- 服务端
在服务端代码中添加业务逻辑代码;
type MysqlServer struct{}
// NewMysqlServer returns a new MysqlServer
func NewMysqlServer() *MysqlServer {
return &MysqlServer{}
}
// SelectRecord selects record with the specific table
func (server *MysqlServer) SelectRecord(
ctx context.Context, req *pb.CreateMysqlRequest,
) (*pb.CreateMysqlResponse, error) {
sql := req.GetSql()
// business logic
// ...
}
创建并启动 gRPC
监听端口,并返回响应客户端请求。
func main() {
// step 1: news a server message struct
mySqlServer := service.NewMysqlServer()
// step 2: difines a listen address
listener, _ := net.Listen("tcp", address)
// setp 3: news a grpc server
grpcServer := grpc.NewServer()
// setp 4: regists grpc server
pb.RegisterMysqlServiceServer(grpcServer, mysqlServer)
reflection.Register(grpcServer)
// step 5: starts grpc server
return grpcServer.Serve(listener)
}
- 客户端
设置连接地址和端口号,调用 gRPC
接口(客户端和服务端可能使用不同的开发语言,需要使用 protoc
编译生成对应的开发语言代码)
func main() {
serverAddress := flag.String("address", "", "the server address")
flag.Parse()
cc1, err := grpc.Dial(*serverAddress)
mysqlClient := client.NewMysqlClient(cc1)
// business logic
log.Printf(mysqlClient.SelectRecord("SELECT * FROM users WHERE id=1;"))
}
到这里一个完整的 gRPC
服务就完成了,那么 gRPC
有哪些优势呢?
gRPC 的优势
- 高效的进程间通信
不同于文本传输的JSON
和XML
,gRPC
使用基于二进制proto buffers
协议进行客户端和服务端之间的通信,它是基于 HTTP2 协议标准实现的,不同进程间通信更加的快捷。
- 服务接口定义更加简单,可读性好
- 支持多种语言,各个系统接入成本低
- 支持双向流模式
- 支持一些常用特性功能
如权限校验,加密通信,服务超时服务截止时间,元数据交换,压缩,负载均衡以及服务发现等等。 - 集成到了云原生生态中
gRPC
作为云原生基金会的一个托管项目,大多数现代框架和技术都提供了对gRPC
的支持。
gRPC 的劣势(相信我总能给你圆回来,哈哈)
- 对于外部服务可能不是适用
有可能对方(本公司)系统不支持gRPC
服务,也有可能不能向对方(非本公司)暴露gRPC
服务。早期gRPC
不支持 HTTP 请求,不过现在这个问题已经解决了,可以通过gRPC
网关将http
请求转为gRPC
请求。这样服务可以支持gRPC
和HTTP
两种请求。 - 对于频繁更迭的服务接口,不是很适用
每次修改服务接口,我们通常需要重新生成客户端和服务端代码,需要将这些代码并入现存的持续集成服务中,这可能使得整体开发周期变得复杂。但是,gRPC
对于不破坏服务约定的修改具有很好的兼容性,允许客户端和服务端使用不同版本的proto
文件,因此代码重新生成也不是什么问题。 - 生态相对较小
和传统的 REST/HTTP 协议相比,gRPC
在浏览器和移动端应用的支持还处于很初级的阶段。
gRPC 支持的四种传输类型
type | request | response | scenario |
unary | unary | unary | normal |
client streaming | streaming | unary | continuous request |
server streaming | unary | streaming | continuous response |
bidirectional streaming | streaming | streaming | continuous request and response |
HTTP/2 versus HTTP/1/1
体验一把:http://www.http2demo.io
gRPC versus REST
参考资料
https://developers.google.com/protocol-buffers/docs/gotutorial
https://www.bilibili.com/video/BV1Xv411t7h5?spm_id_from=333.999.0.0