【问题标题】:Conflict between Play! Framework 2.5 and gRPC 0.13Play 之间的冲突!框架 2.5 和 gRPC 0.13
【发布时间】:2016-06-20 01:28:16
【问题描述】:

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 代码后,它又可以工作了。

我现在可以尝试任何快速修复吗?谢谢!

【问题讨论】:

  • Netty 4.0.33 和 Netty 4.1.0 不是二进制兼容的。您可以尝试force 4.0.33 修订版(不推荐),但我怀疑它会起作用。看起来你需要一个 gRPC 的替代品。
  • @marcospereira force 4.0.33 将无法工作,因为 gRPC 需要 http2,这是 netty 4.1.0 中的新功能。我尝试使用 netty 4.1.0 编译 Play 2.5,但失败了。因此,我现在降级到 Play 2.4。
  • 它适用于 play 2.4 吗?
  • @marcospereira 是的,确实如此。
  • 我遇到了同样的问题,但使用了不同的库:Redisson v3.5.7。我在 Scala 2.11 和 sbt 0.13 上。可能我对此无能为力。

标签: playframework grpc


【解决方案1】:

编辑:

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.Final 版本被4.0.34.Final 版本驱逐,async-http-client 传递添加,这些是 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. 在类级别没有冲突,因为类具有不同的包名(org.jboss.nettyio.netty),然后是不同的完整限定名。因此,它们被视为不同的类。

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

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

【讨论】:

  • 难道不能使用不同的类加载器来解决这个问题,以便每个加载器加载他们需要的任何东西......
  • 所以基本上你会让 play 加载它需要的任何东西,然后为 grpc 创建一个类加载器,并确保任何运行 grpc 东西的线程都使用 grpc 类加载器......可能会很难看
  • 祝你好运,特别是考虑到开发和生产环境。
  • 我需要您对此的意见,我正在考虑这样做。首先,开发/生产环境可能有什么问题?如果它适用于开发,为什么对产品有任何不同?其次,我想到的解决方案是创建一个 Aspect,它将用周围的方面包装任何 grpc 实例化或执行,这将在执行之前设置适当的类加载器并在执行之后设置原始类加载器......你认为这会工作吗?
  • @vach 在这里查看我的最后一次编辑。 Play 2.6.0 将与 Netty 4.1 一起发布。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】: