2021 年接入 Netty 后,原本可用的 gRPC 调用出现过两类错误:

INTERNAL: Connection closed with unknown cause

java.lang.NoSuchMethodError:
io.netty.handler.codec.http2.Http2Connection$Endpoint.maxStreams(II)V

它们来自同日的两条记录,不能仅凭日期相近就断定一定是同一条请求。不过,第二条提供了比“连接关闭”更具体的线索。

NoSuchMethodError 指向二进制兼容

调用方在运行时找不到预期方法,通常要核对编译时依赖与实际加载的类是否一致。它不是普通的网络超时,也不能通过改服务器地址解释。Java NoSuchMethodError

此处方法涉及 Netty 的 HTTP/2 接口,因此应先查 gRPC transport 与 Netty 的组合,再检查最终产物和运行容器。

从解析后的依赖树开始

mvn dependency:tree '-Dincludes=io.grpc:*,io.netty:*,com.google.protobuf:*'

对照 grpc-stub、grpc-protobuf、transport、protobuf runtime 与生成代码所用工具。netty-all、其他框架的传递依赖和 BOM 都可能改变最终选择,不能只看 POM 里某一行。

必要时还可以在受控诊断环境查看实际类来源:

Class<?> type = io.netty.handler.codec.http2.Http2Connection.class;
java.security.CodeSource source =
    type.getProtectionDomain().getCodeSource();
System.out.println(source == null ? "unknown origin" : source.getLocation());

输出路径也应脱敏。容器额外提供的 JAR 或旧打包产物,可能让实际加载结果不同于当前构建声明。

不用一组旧版本号代替兼容验证

当年的一条记录写到 Netty 从 4.1.67.Final 改成 4.1.11.Final 后恢复,另一条又记录 gRPC 升级,但其附带版本片段并不一致。这不足以形成可复用的成功组合,更不是现在推荐降级到旧版本的理由。

应按项目约束成套管理依赖,消除冲突后重新打包验证。gRPC 维护者也讨论过 grpc-netty 与 Netty 的匹配,以及 shaded transport 的隔离方向。grpc-java 维护者讨论

采用 shaded 实现时仍要核对实际使用的 transport 和 API,不能以为换一个依赖就自动解决所有冲突。最后在隔离环境验证连接建立和实际 RPC,并确认没有遗留方法链接错误。