前言
Twirp 是由 Twitch 公司开源的一个 RPC 框架,从 2018 年开源至今一直在国内默默无闻,我认为一个很重要的原因是国内的后端开发仍然停留在 RESTful API 的开发思维里,并没有多少开发者真正转型到 RPC 的开发生态里。并且因为鼎鼎大名的 Google 开源了 gRPC,进一步导致其他 RPC 框架无人问津。本文致力于翻译并精简 twich 公司的一篇博文来介绍这个好用且简单的 RPC 框架。
为什么要使用 RPC
在 2015 年,Twitch 公司做出了巨大的努力去将一个巨无霸 app 拆分成多个独立的服务,在进行微服务拆分的时候,需要考虑很多 API 设计的问题,包括:
如何接收请求,譬如现在要开发一个更新用户 email 地址的接口,那么以下的 RESTful 格式 url 看起来都很合理:
- POST /users/:id/email
- PUT /users/:id/email
- POST(or PUT) /users/:username
- PATCH /users/:username/info
是否要设置一些特殊的 headers
url 的版本号如何体现
上面还只是在创建 API 的阶段需要解决问题,在长期运营 API 的时候还需要处理如下问题:
- 不同开发人员 / 不同开发阶段使用的 url 格式不一致,这样会导致指标采集很难统一处理请求日志,并且很难维护 API 的接口文档
- 服务端代码在版本迭代之后,你需要手写去更新客户端的代码
Twirp 通过代码自动生成的方法来解决上述的问题,用户只需要编写一个简洁的 protobuf 文件来描述 API,Twirp 会生成一个go 文件,主要包含了 HTTP 的处理代码(包括路由,序列化等),举例如下:
syntax = “proto3”; |
Twirp 生成的 go 文件主要包括以下内容
type EmailBoss interface { |
有了这个自动生成的 go 文件,后端开发人员就不需要再考虑 url schema 等问题,只需要专注于实现接口的处理逻辑。而 client 也只需要指定 server 的 ip 和端口,就可以直接使用生成的实例来调用 server 的服务。
Twirp 生成的所有请求都是基于 Http1.1,并且只能使用 POST 请求提交参数,payload 既可以是 JSON,也可以是二进制编译的 protobuf。因此,对于上面的例子来说:
- url 为:/Twirp/Twitch.users.email.EmailBoss/UpdateEmail
- http 请求方法为 POST,并且只能为 POST
- 请求的 payload 如果使用 json 格式,header 需要指定为:Content-Type: application/json,如果使用 protobuf,指定为:application/protobuf
- 如果请求出现问题,server 返回的 response 同样用json 编码,会包含出错信息和标准的错误码
为什么不使用 gRPC
Google 提供的 gRPC 框架同样也会自动生成代码,Twitch 公司一开始也是使用 gRPC,但遇到了四个核心的问题是 gRPC 无法解决的,从而创建了 Twirp 框架:
- 不支持 HTTP1.1,gRPC 只支持 http/2,因为它依赖http/2 来提供全双工流,但 Twirp 可以同时支持 http1.1 和 http/2。因为很多负载均衡(软件和硬件,譬如 AWS
的 ELB)现阶段都只支持 http1.1。Twirp 最大的缺点就是不支持流式 RPC,但 Twitch 发现绝大部分的请求都不需要使用流式 rpc - gRPC 生成的 go 代码比较少,因此调用了一个大型的运行时库:grpc-go,不幸的是,这个库偶尔会出现重大更新,就会导致无法编译旧的客户端代码,从而需要大规模更新客户端代码才能保证 gRPC 版本的一致性。而 Twirp 生成的代码里几乎包含了所有需要用到的代码,并且尽量少地进行重大版本更新,以此来保证旧系统的平稳运行。
- grpc-go 并没有使用标准的网络库,而是自己实现了一个完整的 http/2 ,以此引入了自定义的网络通信控制机制。但这样的做法也会引入一些其他人难以理解的代码,并引发bug。
- gRPC 只支持二进制格式的 protobuf payload,而 Twirp 还另外支持 json 格式的 payload,这样的好处有:1、易于编写跨语言的客户端代码,毕竟 Restful API 也是使用 json 格式的 payload。2、很容易使用 cURL 工具来调试服务端代码。而 gRPC 使用的二进制 protobuf payload 使用起来会感觉完全不透明
总的来说,gRPC 最大的优势就是支持双向流式 RPC,客户端和服务端之间可以不间断地发送数据流,而 Twirp 没有这样的功能,只支持一问一答的交互方式,在 Twitch 公司里并没有一定要使用 gRPC 双向流式 RPC 的场景。并且 grpc-go 还集成了一篮子的插件,从负载均衡到服务发现,新的思想意味新的学习成本。
Twirp 在生产环境的表现
总结上文,如果你在编写后台服务,使用结构化的 rpc 要比自己手动编写 Restful API 更好,Twitch 已经使用 Twirp 来编写大量的后台系统。Twirp 的简洁性尤其值得称赞,因为你只需要专注于后端的代码逻辑,而不用纠结这个请求应该使用 PUT / POST 还是 DELETE 请求。简洁意味着稳定,Twitch 尚未观测到任何一例因为 Twirp 的 bug 带来的业务中断,因为 Twirp 本身没有太多可能引发故障的情况。