0%

Java与Go之间gRPC调用失败问题排查

最近接手了一个新项目,被告知Java代码的gRPC客户端无法访问当前项目中Go代码的gRPC服务。通过问题的排查和部分代码改造个人对gRPC的理解更深入了一层.

项目背景:

接手该项目后跟该项目相关人员多次沟通后得到如下背景信息:

  1. 该项目Go服务端采用的是2016年9月以前发布的gRPC ;
  2. 项目组对官方提供的Go语言gRPC类库做了部分改造,但,由于改造该代码的工程师已经离职,目前没有人知道具体修改了什么​;
  3. 由于项目周期紧张,没有富余的人力排查该问题;
  4. 目前Java与Go互相交互的接口不到10个,双方先采用HTTP交互;
  5. 服务端总是需要维护两套接口,HTTP和gRPC接口,链路跟踪等公共组件都要适配两套接口,增加了维护成本

问题重现

  1. 采用Java版本gRPC-Java-1.18.0访问现有的Go服务,请求发出以后被hang住没有任何反馈直到客户端设置的连接超时后断开,Java客户端和Go服务端没有任何输出信息;
  2. 抓包看包文发现在完成TCP的3次握手后,客户端发送了第一HTTP2的包文后,没有收到服务端的任何回复,整个过程被hang;初步估计是双方采用的HTTP2协议不兼容。
  3. 采用低版本的Java gRPC, 调研发现Java的gRPC在2016年9月以前的最新版本是1.0.0,所以从1.0.0开始测试,最终发现0.9.0可以完成正常请求。

问题原因

调研发现0.9.0版本的Java gRPC依赖了4.1.0.Beta6的Netty,该Netty版本发布与2015年9月, 而HTTP2在2015年有多次修订,版本号也升级到了17;我估计该版本的Netty是根据HTTP2修订版17以前的规范实现的,而且该版本为Beta版,导致跟其它语言的协议存在某些差异。

其它问题

采用0.9.0版本的gRPC后会带来以下新问题:

  1. 现有的Java项目框架采用springcloud2.0.0,该版本依赖了Netty的4.1.27.Final版本,项目集成后会出现版本冲突导致各种异常出现;
  2. 0.9.0版本的gRPC缺少很多重要的特性,比如:nameresolver,负载均衡 等 …
  3. 最终还是要升级到新版本

解决方案:

综合沟通下来最好的方案就是Go服务端升级gRPC到新版本 ,但是团队将官方代码改造后已经无法做到无缝升级,必须要做部分改造才能投入生产​;目前生产环境运行的Go服务超过50+,而且大部分是核心服务;改造后需要充分的测试,总体成本太大,而且目前没有更多的资源投入。最终决定才有用低版本的Java gRPC​,采用以下的措施应对低版本面临的问题:

  1. 所有的Java项目提供统一的parent pom文件,所有的版本信息在parent pom的dependencyManagement中维护;
  2. 增加新功能:nameresolver,负载均衡,兼容Go服务的服务发现,等…

一点思考

我们在技术选型的不能过于保守也不能过于激进;太过保守不能充分利用新技术的优势,甚至会影响到团队士气;太过激进会遭遇太多的不确定性,面临着遇到问题没有先例可以参考,遭遇新的bug,等;所以,在团队没有足够的技术支撑的情况下,我不太建议生产环境采用1.0以下的版本。作为技术人员应该随时关注业界的新技术,但在生产环境采用新技术前必须考虑清楚所面对的各种风险,包括以后的升级能否做到无缝升级。​

参考资料:

https://grpc.io/
https://github.com/grpc/grpc-java
https://datatracker.ietf.org/doc/draft-ietf-httpbis-http2/history/