V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
Ley

k8s 上 Java 服务随机抛出 NoSuchMethodError 异场

  •  
  •   Ley · Aug 29, 2019 · 8109 views
    This topic created in 2446 days ago, the information mentioned may be changed or developed.

    最近看了几天的问题,Google、文档后无果,来论坛上请教是否有人有解决思路。

    我们使用 k8s host 某个 Java 项目,使用 jetty,项目通过 maven 管理依赖。之前一直正常,但最近遇到问题。 神奇的是,即使是同一份 image,每次 set 之后的表现也会不同。有时一切正常,有时则会在调用特定方法时抛出如下异常。 java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.elapsed()Ljava/time/Duration;

    这个问题看起来是项目中不同模块依赖不同的 guava 版本所致。一个我们依赖的第三方包需要依赖 22.0 版本的 guava,但其他一些依赖会找到比较老的版本。那些老版本缺少 elapsed() 方法。为此,我们在数月前曾经通过 shade 的 plugin 让依赖该包的项目始终使用 22.0 版本。一切相安无事。 最近两周,突然开始出现 mvn package 并 build 成 docker image 后放到 k8s 上,会随机发生上述异常。检查了编译结果,似乎和以前没有区别。至少,在需要依赖 22.0 版本的项目 jar 包中,guava 22.0 依然被 shade。 使用更新版本的 guava 似乎可以解决这个问题,但这显然掩盖了问题的根本原因。

    有怀疑过 k8s 是否会缓存一些依赖,但看起来又不像,也查不到类似的文档。

    不知诸位对这个问题有何高见?

    1 replies    2019-09-29 20:52:27 +08:00
    Ley
        1
    Ley  
    OP
       Sep 29, 2019
    最后通过增加额外逻辑在 class loader 打印出 classpath 发现了问题所在。
    原因是某个 Apache 的依赖内直接包装了老版本的 com.google.common.base.Stopwatch,因此在 mvn 的 dependency tree 中不会被列出。然后在 Jetty 的 embeded server 中 load class 时会在 mvn 中的版本和这个老版本中随机选择一个。如果选中了不包含 elapsed()Ljava/time/Duration 的版本,就会抛出异常。
    由于该 Apache 依赖仅用于单元测试,将其 dependency scope 改为 test 便解决问题。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5697 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 07:56 · PVG 15:56 · LAX 00:56 · JFK 03:56
    ♥ Do have faith in what you're doing.