目的
实现后端微服务架构中 TS 与 Python 服务之间的通信,希望能通过维护同一套类型定义/ Schema 保证 single source of truth ,提高接口可维护性。
尝试过的方案
- 最简单的方案,Python 服务用 Flask 实现普通的 REST API 来对其他模块提供服务。问题是一方接口定义更改时,另一方需要同步更改,如果是纯 TS 项目的话,得益于类型信息共享,先改类型定义,所有受影响的地方都能及时发现,后改相关代码就很方便,也比较安心,但如果是多语言的话做不到这点
- 借此机会想试试 RPC 替代 REST ,于是尝试了一下 gRPC ,本身多语言支持,还有通用统一的 Protobuf ,看上去很美好,然而可能是我太菜不熟悉,感觉这玩意用起来槽点太多了……
使用 gRPC 时遇到的问题
- 首先最让我感到意外的一点是,那么多官方支持的语言里面,居然没有 JS (还是在连 Dart 和 Ruby 都有的情况下)。虽然有个
grpc/grpc-node,但对 JS 生态的支持看上去不是特别理想 -
Protobuf 生成的代码质量一言难尽:
-
在 TS 项目中,想按照官方文档里的例子那样使用
@grpc/grpc-js的话,需要自动生成类型定义。找了一圈,发现只有@grpc/proto-loader自带的proto-loader-gen-types生成的还算可用,然而还是有坑,比如引入路径中没有文件扩展名,而在 TS 中使用 ESM +module": "node16"时文件扩展名是强制的,所以还是需要手动修改生成的代码。另外有个@bufbuild/protobuf,号称是唯一一个全部通过 Protobuf 兼容性测试的,结果生成的东西@grpc/grpc-js压根用不了,只能用他们的配套库@connectrpc/connect,说是兼容 gRPC ,也只是客户端部分可以发起 gRPC 而已 -
用
protoc生成的 Python 代码也有引入路径问题,如果文件不在根目录下,就会报错找不到模块,于是你还是需要手动修改自动生成的代码 -
官方文档太差了,没有更多用例来解释真实场景里是什么样的,比如服务端的部分:
server.add_insecure_port("[::]:" + PORT)server.bindAsync( `0.0.0.0:${PORT}`, gRPC.ServerCredentials.createInsecure(), (err, port) => {}, );这都 insecure 了,那生产环境中想 secure 该怎么办?
-
上面的问题 GitHub 也有相关 Issue 讨论,但到目前为止似乎都不了了之了。想问一下有相关经验的佬们:
- 我这个需求用 gRPC 是否合适?除此之外还有其他解决方案吗?
- 在 TS 和 Python 下,正确的使用姿势分别是什么?除了裸用官方的库
@grpc/grpc-js/grpcio之外,还有其他基于 gRPC 的更好用的框架吗?