目的
实现后端微服务架构中 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 的更好用的框架吗?

用 openapi ,然后生成 ts/python 客户端代码?感觉使用应该简单一点(@hey-api/openapi-ts 和 openapi-python-client ),方案比 grpc 简单点,都是 restful api ,不过性能肯定是比不了了

这个方案靠谱,jsonschema 库还是稳定的

用 protobuf 也依然需要另一端同步修改吧
难道你不用给 proto 文件就可以直接用?

/grpc-js 我记得是动态 hook proto 文件,类型检查的很好,什么时候退化了吗。gRPC.ServerCredentials.createInsecure() 这一句只是指 grpc 的 tcp 是裸传输的,能中间抓包然后 proto 反解到传输的是什么。要加 tls ,得使用 grpc.ServerCredentials.createSsl() 的方法。

从楼主的整个思路来看,还是 rest api 吧,整个查资料的深度不足以应付后面的 grpc 相关的更大的坑。

什么是 gRPC, 就是说 Google RPC, 咱要不是能和 Google 比划比划的话,还是老老实实 HTTP 吧

python 的那个导入问题,翻了翻书签,应该和这个 issue 有关系 github.com/protocolbuffers/protobuf/issues/1491 用 google 的东西,你得顺从 google 那一套,并且勤翻 issue 区和源码。

用 ts-proto 更好,我们已经大范围使用了

grpc 太复杂了,依赖库版本和 protoc 也一直更新,保不齐哪天就不兼容了,生成出来的代码每次 diff 一大片也挺难受的(我这边是 golang 用的,改旧项目的 grpc 简直爆炸,根本就一点都不想维护)。 简单的远程调用,我感觉还是用 http 吧,想提升性能可以用 http2 ?毕竟 grpc 也是基于 http2 的

顺带 protobuf 最大优势也就是性能上,多项目、多人维护的时候,工作流程上也是脑袋都大了。

不好用是因为没有人用 TS/python 搭配 grpc 。gprc 主要就是提升传输效率,但你传输层提升半天最后应用层进 node/v8 这种运行时又会变很慢。就像开 F1 下午五点去东三环一样,意义微乎其微。

grpc 的易用性从来就没好过吧,用它团队里总得有几个人头疼。

其实 jsonschema 在两边都能找到可用的生成器,我觉得好于 grpc

1 、protobuf 本身就很糟糕,如果你不是谷歌那种全公司代码在一个仓库里的管理方式,建议别用
2 、绝大部分的 RPC 需求用 JSON-RPC 就能满足,性能瓶颈根本不在 RPC 库上

你一边接口的参数/返回数据的结构变了,用什么 rpc 协议都不能让你不用改调用者的逻辑就能实现同步啊。

感谢各位的解答,我再去看看 json schema

用 connectrpc 支持 grpc, buf 算是 proto 这块很好用的工具了, buf.build/explore

这个我也看了,好像只支持 TS 做客户端发 gPRC 请求,做不了服务端?如此的话 Python 那块还是没法解决

#17 connectrpc.com/docs/node/getting-started node 版本不是可以作为 server 吗?当然我对 js 很了解

一样的用法,看了下我 ts 这边用的是 grpc_tools_node_protoc_plugin ,生成的代码没动过就可以带 type
反倒是 python 这边生成的代码过不了 lint ,而且 import 确实有问题,得自己手改一下

不是吧,不理解楼上的人的说法,即使 protobuf ,gRPC 不好用,也总能用吧,不至于用不到吧?另外,每次改动后,生成一大片 diff? 是有 diff ,也没多大啊

ts 里面用 GRPC 确实比较难受。之前做出过 ts 服务端和 Android (kt) 客户端的通信,kt 里面有明确的接入步骤和官方的插件生成产物。ts 的话也有不少插件但是能很舒服用的不多。目前在用的是 grpc_tools_node_protoc_ts 、ts-proto 、ts-protoc-gen 这几个。