Netty入门-组件EventLoop

3、组件

3.1、EventLoop

事件循环对象EventLop

EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。

继承关系:

  • 一条线是继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法
  • 另一条线是继承自 netty 自己的 OrderedEventExecutor,
    • 提供了 boolean inEventLoop(Thread thread) 方法判断一个线程是否属于此 EventLoop
    • 提供了 parent 方法来看看自己属于哪个 EventLoopGroup

事件循环组EventLoopGroup

EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)

继承自 netty 自己的 EventExecutorGroup

  • 实现了 Iterable 接口提供遍历 EventLoop 的能力
  • 另有 next 方法获取集合中下一个 EventLoop

代码

@Slf4j
public class TestEventLoop {
    public static void main(String[] args) {
        //1. 创建事件循环组
        EventLoopGroup group = new NioEventLoopGroup(2);//io 事件,普通任务,定时恩物
        EventLoopGroup group1 = new DefaultEventLoopGroup();//普通任务,定时任务
        //2. 循环获取事件对象,轮询,1和3一样
        System.out.println(group.next());//1
        System.out.println(group.next());//2
        System.out.println(group.next());//3
        //3. 执行普通任务,异步的
        group.next().submit(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("普通任务..");
        });
        log.debug("main..");
        //4. 执行定时任务
        group.next().scheduleAtFixedRate(() -> {
            log.debug("定时任务..");
        }, 0, 1, TimeUnit.SECONDS);
    }
}

IO任务代码

//服务端
//**.group方法建议指定两个EventLoopGroup,第一个就是boss,第二个就是worker**
@Slf4j
public class EventLoopServer {
    public static void main(String[] args) {
        new ServerBootstrap()
                //.group(new NioEventLoopGroup())
                .group(new NioEventLoopGroup(), new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel) throws Exception {
                        channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override//此处没有添加解码器,所以msg还是ByteBuf类型
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                log.debug(buf.toString(Charset.defaultCharset()));
                            }
                        });
                    }
                })
                .bind(8080);
    }
}

//客户端
public class EventLoopClient {
    public static void main(String[] args) throws InterruptedException {
        //1. 启动类
        Channel channel = new Bootstrap()
                //2. 添加EventLoop
                .group(new NioEventLoopGroup())
                //3. 选择客户端channel实现
                .channel(NioSocketChannel.class)
                //4. 添加处理器
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    //在连接建立后被调用
                    protected void initChannel(NioSocketChannel channel) throws Exception {
                        //编码器,将要发送的消息转为ByteBuf
                        channel.pipeline().addLast(new StringEncoder());
                    }
                })
                //5. 连接到服务器
                .connect(new InetSocketAddress(8080))
                //阻塞方法,直到连接建立
                .sync()
                //代表连接对象
                .channel();
        System.out.println(channel);
        System.out.println("=====");//此处加断点
    }
}

客户端加断点的地方注意点,

需要由All改为Thread,因为netty客户端主线程和发数据的线程并不是同一个线程。

开启多个客户端进入debug模式,发现每个客户端都与一个EventLoop进行了绑定,工人(EventLoop)与channel进行了绑定。

EventLoop与channel是一对多的关系

优化

  • 1.EventLoopGroup分工细化

    //第一个参数就是boss,第二个参数就是worker
    .group(new NioEventLoopGroup(), new NioEventLoopGroup())
    

    为了测试多个channel可以共用一个EventLoop,可以将worker线程数据进行手动new NioEventLoopGroup(2),代表只有两个worker,然后启动三个以上客户端,debug测试,观察结果,发现client-0和client-2共用了EventLoop-3-1,client与EventLoop就是多对一的关系

  • 2.如果处理消息过于复杂,耗时,就会影响netty内的EventLoop,改进:添加自定义的EvnetLoop,将消息从netty内传递到自定义的EventLoop处理

    @Slf4j
    public class EventLoopServer {
        public static void main(String[] args) {
    
            EventLoopGroup group = new DefaultEventLoopGroup();
    
            new ServerBootstrap()
                    //.group(new NioEventLoopGroup())
                    //细化1:boss和worker分开
                    .group(new NioEventLoopGroup(), new NioEventLoopGroup(2))
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel channel) throws Exception {
                            channel.pipeline()
                                    .addLast("handler1", new ChannelInboundHandlerAdapter() {
                                        @Override//此处没有添加解码器,所以msg还是ByteBuf类型
                                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                            ByteBuf buf = (ByteBuf) msg;
                                            log.debug("传递前:"+buf.toString(Charset.defaultCharset()));
                                            ctx.fireChannelRead(msg);
                                        }
                                    })
                                    //细化2:添加自定义handler,group
                                    .addLast(group, "handler2", new ChannelInboundHandlerAdapter() {
                                        @Override
                                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                            ByteBuf buf = (ByteBuf) msg;
                                            log.debug("传递后:"+buf.toString(Charset.defaultCharset()));
                                        }
                                    });
                        }
                    })
                    .bind(8080);
        }
    }
    

传递前:用的nioEventLoop,传递后用的自定义的defaultEventLoopGroup,而defaultEventLoopGroup没有指定线程数,就是CPU核心数*2,三个客户端轮询使用nioEventLoopGroup

handler执行中如何换人

源码:io.netty.channel.AbstractChannelHandlerContext

static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
    final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
    //executor方法返回的是下一个handler的eventLoop,EventExecutor是EventLoop的父类,所以他两是一个东西
    EventExecutor executor = next.executor();
    
    //如果当前handler的线程与下一个eventLoop是用一个线程,是就直接调用
    if (executor.inEventLoop()) {
        next.invokeChannelRead(m);
        
    //否则,将要执行的代码作为任务提交给下一个时间循环处理(换人)
    //execute(new Runnable() 方法就是开启了一个新线程,而这个线程及时nex的线程
    } else {
        executor.execute(new Runnable() {
            public void run() {
                next.invokeChannelRead(m);
            }
        });
    }
}
  • 如果两个 handler 绑定的是同一个线程,那么就直接调用
  • 否则,把要调用的代码封装为一个任务对象,由下一个 handler 的线程来调用
posted @   jpy  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示