前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >应用依赖不同的Netty版本引发的错误

应用依赖不同的Netty版本引发的错误

作者头像
书唐瑞
发布2022-06-02 15:02:23
3.4K0
发布2022-06-02 15:02:23
举报
文章被收录于专栏:Netty历险记Netty历险记

昨晚服务在发布的时候, 出现如下异常

Caused by: java.lang.NoSuchMethodError: ...

Dubbo在暴露服务的时候, 需要启动Netty服务端, 在启动服务端的过程中, 根据Reactor模型, 它需要创建IO线程.会涉及到使用Netty中的

io.netty.util.concurrent.SingleThreadEventExecutor类, 根据错误提示, 在构造SingleThreadEventExecutor对象的时候, 找不到符合的构造器方法.

查看下应用依赖的Netty包

虽然有2个3.x版本的Netty包, 但是3.x版本的Netty包名都是 org.jboss.netty, 4.x版本的包名都是io.netty, 根据错误提示的包名, 因此排除3.x版本的嫌疑.

剩下的就是4.1.43版本和4.1.29版本, 版本不一致, 很可能就是因为这个原因造成的.

io.netty.util.concurrent.SingleThreadEventExecutor 这个类出现在两个包里.

netty-all-4.1.43.Final.jar 和 netty-common-4.1.29.Final.jar 包中都有SingleThreadEventExecutor 类.

写了一个简单的测试案例

代码语言:javascript
复制
// Example.java

package com.infuq;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;

public class Example {

    public static void main(String[] args) throws Exception {
        // 加载SingleThreadEventExecutor类
        Class.forName("io.netty.util.concurrent.SingleThreadEventExecutor");

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(8);
        EventLoopGroup businessGroup = new NioEventLoopGroup(8);

        ServerBootstrap serverBootstrap = new ServerBootstrap();

        try {

            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) {
                            ChannelPipeline channelPipeline = ch.pipeline();

                            channelPipeline.addLast(new StringEncoder());
                            channelPipeline.addLast(new StringDecoder());
                            channelPipeline.addLast("idleEventHandler", new IdleStateHandler(0, 10, 0));
                            channelPipeline.addAfter("idleEventHandler","loggingHandler",new LoggingHandler(LogLevel.INF
O));
                        }
                    });

            ChannelFuture channelFuture = serverBootstrap.bind("127.0.0.1", 8080).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

以上代码会使用Netty创建一个服务端, 也是在模拟Dubbo使用Netty创建服务端, 本质是一样的. 只是在我的代码中, 使用

代码语言:javascript
复制
Class.forName("io.netty.util.concurrent.SingleThreadEventExecutor");

手动提前加载SingleThreadEventExecutor类.

编译程序

代码语言:javascript
复制
javac -d . -classpath ".:./netty-all-4.1.43.Final.jar:./netty-common-4.1.29.Final.jar" Example.java

在这里我们手动指定了jar包的加载顺序

运行程序

服务正常启动了...

接下来改变一下编译与运行时加载Jar包的顺序, 让类加载器在加载SingleThreadEventExecutor类的时候, 先从netty-common-4.1.29.Final.jar包中查找加载.

编译

代码语言:javascript
复制
javac -d . -classpath ".:./netty-common-4.1.29.Final.jar:./netty-all-4.1.43.Final.jar" Example.java

运行

出现了与文章一开始一样的错误. 因为提前加载了netty-common-4.1.29.Final.jar版本中的SingleThreadEventExecutor类, 而接下来创建Netty服务端的时候, 在构造SingleThreadEventExecutor对象的时候, 传入的参数格式是按照netty-all-4.1.43.Final.jar包中的SingleThreadEventExecutor类传参. netty-common-4.1.29.Final.jar 和 netty-all-4.1.43.Final.jar 中关于SingleThreadEventExecutor类构造器的确不同, 如下

netty-all-4.1.43.Final.jar 包中的SingleThreadEventExecutor类构造器比netty-common-4.1.29.Final.jar包中的SingleThreadEventExecutor类构造器多一个, 而且就是错误中提示的`缺失`那个构造器.

使用mvn dependency:tree > tmp.txt命令导出来依赖关系, 查看了下, netty-common-4.1.29.Final.jar 和 netty-all-4.1.43.Final.jar 这两个包分别是被架构组A和团队B使用, 而作为使用方的我们, 需要手动解决版本不一样的问题, 否则就会出现许多莫名其妙错误.

在这之前应用没有出现过类似错误, 所以感觉很奇怪, 为什么最近突然出现了这样的错误, 原来是我们最近代码中接入了团队B的一个能力框架, 它的底层依赖了Netty, 只是版本与我们代码中依赖架构组A使用的Netty版本不一致引起的.

世界大同, 版本一致是原则.

问题似乎找到了, 但似乎又没有找到, 虽然知道是因为版本不同导致的, 然而是哪块代码提前类加载了netty-common-4.1.29.Final.jar包中的SingleThreadEventExecutor类呢, Spring类加载jar的顺序是怎样? 难道不是按照我们使用ls -l命令查看到的Jar顺序加载的吗? SpringBoot应用的classpath.idx文件作用是什么? 这些都是待解的疑惑.

解压springboot的jar包就会看到classpath.idx文件

下次见

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Netty历险记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档