播放之间的冲突!框架 2.5 和 gRPC 0.13

Conflict between Play! Framework 2.5 and gRPC 0.13

Play 2.5.0 使用 Netty 4.0.33,而 gRPC 需要 Netty 4.1.0(用于 http2 支持),这会导致以下异常:

[error] p.c.s.n.PlayRequestHandler - Exception caught in Netty
java.lang.AbstractMethodError: null
    at io.netty.util.ReferenceCountUtil.touch(ReferenceCountUtil.java:73)
    at io.netty.channel.DefaultChannelPipeline.touch(DefaultChannelPipeline.java:84)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:154)
    at com.typesafe.netty.http.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:131)
    at com.typesafe.netty.http.HttpStreamsServerHandler.channelRead(HttpStreamsServerHandler.java:96)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:154)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
[error] p.c.s.n.PlayRequestHandler - Exception caught in Netty
java.util.NoSuchElementException: http-handler-body-publisher
    at io.netty.channel.DefaultChannelPipeline.getContextOrDie(DefaultChannelPipeline.java:1050)
    at io.netty.channel.DefaultChannelPipeline.remove(DefaultChannelPipeline.java:379)
    at com.typesafe.netty.http.HttpStreamsHandler.handleReadHttpContent(HttpStreamsHandler.java:191)
    at com.typesafe.netty.http.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:167)
    at com.typesafe.netty.http.HttpStreamsServerHandler.channelRead(HttpStreamsServerHandler.java:96)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    at io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:154)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
    at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)

在我删除所有 gRPC 代码后,它又可以工作了。

有没有我现在可以尝试的快速修复方法?谢谢!

编辑:

Play 2.6.0 was released,并且它使用的是 Netty 4.1。

tl;博士

由于 Netty 4 和 Netty 4.1 之间的二进制不兼容,Play 2.5.0 和 gRPC 不兼容。


以下是它不适用于 Play 2.5.0 但适用于 Play 2.4.0 的原因:

Play 2.5.0 使用 Netty 版本 4.0.33.Final,声明如下(通过 netty-reactive-streams version 1.0.2 传递):

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-handler</artifactId>
    <version>4.0.33.Final</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-codec-http</artifactId>
    <version>4.0.33.Final</version>
</dependency>

版本 4.0.33.Finalasync-http-client 传递添加的版本 4.0.34.Final 驱逐,这些是 Play 2.5.0 使用的 netty 依赖项:

io.netty:netty-buffer:4.0.34.Final
io.netty:netty-codec-http:4.0.34.Final
io.netty:netty-codec:4.0.34.Final
io.netty:netty-common:4.0.34.Final
io.netty:netty-handler:4.0.34.Final
io.netty:netty-transport:4.0.34.Final

另一方面,Play 2.4.6 只需要以下 netty 依赖项:

io.netty:netty:3.8.0.Final

因此,虽然 Play 2.5.0 依赖于许多较小的 netty 包,但 Play 2.4.6 仅依赖于一个 netty 包。这是因为 Netty 4 changed the project structure 将项目拆分为多个子项目,以便用户可以从 Netty 添加必要的功能。更重要的是,“Netty 的包名称已从 org.jboss.netty 更改为 io.netty”。

为什么这些更改都与此相关?

  1. gRPC 与 2.4.6 之间没有依赖冲突,因为 gRPC 不需要依赖 io.netty:netty,甚至不需要传递依赖。因此,没有驱逐。
  2. class 级别没有冲突,因为 classes 有不同的包名称(org.jboss.nettyio.netty),然后是不同的全限定名称。因此,它们被视为不同的 classes.

与 Play 2.5.0 存在冲突,因为 Netty 4 和 Netty 4.1 具有相同的依赖项 artifactId(然后 4.1 版本驱逐 4.0.34)并且因为 - 因为我们现在有 相同 这两个版本之间的完全限定 class 名称 - 二进制文件不兼容。

这是一个很长的解释,说现在没有办法让 gRPC 和 Play 2.5.0 与 Netty 一起工作。即使决定使用Akka HTTP server backend,也有可能与Play WS发生冲突

我在 Play using Java 中也遇到过这个问题。您使用的是 gRPC 客户端还是服务器接口与 Play 服务器?如果你只是使用一个客户端,你可以通过使用 OKHTTP 客户端版本而不是依赖 Netty 的版本来克服这个问题。

https://mvnrepository.com/artifact/io.grpc/grpc-okhttp

如果您使用的是服务器,我想您可能不太走运。您可以尝试从 gRPC 中排除 Netty,并希望 Play 的版本足够。只需将 excludeAll ExclusionRule(organization = "io.netty") 添加到 gRPC 导入。

这是关于该主题的 play 邮件列表中的一个线程,但他们目前对该主题没有任何更改: https://groups.google.com/forum/#!topic/play-framework/TWa18IfZ5kA

您还可以使用 maven-shade-plugin 重新定位 netty 包名称,以免与早期版本冲突。

查看 couchbase jvm 核心库示例 https://github.com/couchbase/couchbase-jvm-core