某个应用在日常环境运行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优先找到了。