tomcat启动遇到NoSuchMethodError错误的排查思路

某个应用在日常环境运行ok,在预发布时却启动错误,异常日志如下:

Caused by: java.lang.NoSuchMethodError: 
org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory.
<init>(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;II)V

at com.taobao.xxx.client.rpc.net.XXXConnector.<init>(XXXConnector.java:43)

是调用netty的NioClientSocketChannelFactory的构造方法时找不到对应的方法,根据提示信息,这个方法的签名应该是这样:

NioClientSocketChannelFactory(Executor, Executor, int, int) 

绝大部分情况下,都是因为多个版本的jar包同时存在产生的冲突,去用户的war下搜索一下。

 for file in *.jar; do 
    echo $file;
    /opt/taobao/java/bin/jar tvf $file | grep NioClientSocketChannelFactory;  
 done

找到了两个jar里都包含那个类,这两个jar也是两个不同的netty版本:

netty-3.6.3.Final.jar
jboss.jboss-netty-3.2.5.Final.jar

分别解开,看看哪个版本里是有那个构造方法的,先看3.6.3 版本:

$/opt/taobao/java/bin/javap  org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory | grep Executor | grep int

public org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory(java.util.concurrent.Executor, java.util.concurrent.Executor, int);
public org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory(java.util.concurrent.Executor, java.util.concurrent.Executor, int, int);
public org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory(java.util.concurrent.Executor, int, org.jboss.netty.channel.socket.nio.WorkerPool);
public org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory(java.util.concurrent.Executor, int, org.jboss.netty.channel.socket.nio.WorkerPool, org.jboss.netty.util.Timer);

里面是包含了我们需要的那个方法的。再看 3.2.5 版本:

$/opt/taobao/java/bin/javap  org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory | grep Executor | grep int

public org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory(java.util.concurrent.Executor, java.util.concurrent.Executor, int);

签名里只有1个int参数,没有我们要用的那个。

可以判断这次是因为classloader优先加载了jboss.jboss-netty-3.2.5.Final.jar这个文件而导致问题的,那为何日常环境的机器上却没问题呢?对于classloader而言,找文件的过程取决于文件系统返回的顺序,简单的说,在linux上取决于两个inode的顺序,在这台预发布机器上,正好 3.2.5 版本的 inode 在前:

$ stat netty-3.6.3.Final.jar
  File: `netty-3.6.3.Final.jar'
  Size: 1202373     Blocks: 2368       IO Block: 4096   regular file
Device: 805h/2053d  Inode: 66912666    Links: 1
Access: (0644/-rw-r--r--)  Uid: (  594/   admin)   Gid: (  594/   admin)
Access: 2014-08-06 13:21:56.000000000 +0800
Modify: 2014-07-14 16:13:44.000000000 +0800
Change: 2014-08-06 13:21:54.000000000 +0800    

$ stat jboss.jboss-netty-3.2.5.Final.jar
  File: `jboss.jboss-netty-3.2.5.Final.jar'
  Size: 792314      Blocks: 1568       IO Block: 4096   regular file
Device: 805h/2053d  Inode: 66912474    Links: 1
Access: (0644/-rw-r--r--)  Uid: (  594/   admin)   Gid: (  594/   admin)
Access: 2014-08-06 13:21:56.000000000 +0800
Modify: 2014-07-14 16:14:18.000000000 +0800
Change: 2014-08-06 13:21:53.000000000 +0800

3.2.5版本的jar文件inode小于3.6.3版本的jar文件,被classloader优先找到了。

tomcat启动遇到NoSuchMethodError错误的排查思路》上有6条评论

  1. ayanamist

    可以用jboss的tattletale maven插件,检查命名空间冲突的问题,将问题扼杀在打包之前

    回复
  2. hongjiang 文章作者

    更新一下,有人告诉我说Linux上扫描目录下的文件时,并不是按照inode,而是按照存储位置的offset 。

    回复
  3. duanjinjin

    有点疑问,那先加载了netty-3.6.3.Final.jar,就不会再加载 jboss.jboss-netty-3.2.5.Final.jar吗?

    回复
    1. 谢晞鸣

      jboss的jar包还会再加载,但是对于org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory这个类,不会再加载了

      回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注