netty EventLoop 源码分析(一)

Netty 核心代码分析

背景

都说是 netty 的 write() 在非 EventLoop 线程调用时线程安全的。但是没看过源码,总是觉得不安心。可能之前自己在写 Reactor 的时候也总是在考虑怎么解决 Reactor 线程 和普通线程 write() 怎么解决竞争问题。所以抱着学习的态度学习了下 Netty 的 EventLoop 对各种事件的处理过程(包括了我一直纠结的 write()。

准备知识

java nio

这不是 java nio 的教程,所以我们只是描述下主要流程(不仅仅是 java 语言,其他的编程语言实现多路复用基本上步骤是一样的)。

1.初始化一个多路复用器

selector = Selector.open() 

2.注册感兴趣的 channel 事件。

channel.register(selector, SelectionKey.OP_READ| SelectionKey.OP_WRITE);  

3.等待事件发生并处理

while(true){
                if(selector.select(TIMEOUT) == 0){
                    System.out.println("==");
                    continue;
                }
                //遍历处理所有的事件
                Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                while(iter.hasNext()){
                    SelectionKey key = iter.next();
                    //准备好接受新链接
                    if(key.isAcceptable()){
                        handleAccept(key);
                    }
                    //处理可读事件的请求
                    if(key.isReadable()){
                        handleRead(key);
                    }
                    //处理可写事件的请求
                    if(key.isWritable() && key.isValid()){
                        handleWrite(key);
                    }
                    iter.remove();
                }
            }

EventLoop 的类图

先看下 NioEventLoop 的类图。通过这个类图你只需要记住一点就是 NioEventLoop 是单线程,有序处理任务的线程池就好了。
这里写图片描述

NioEventLoop 核心代码

SingleThreadEventExecutor 是一个线程池,所以 excute() 会执行。
excute() 执行最终会调用子类 NioEventLoop 的 run() 方法。

NioEventLoop 核心部分 run()。

    //ioRate 是指此线程执行 io 操作占时百分比,默认是 50
    protected void run() {
        for (;;) {
            try {
                //没有任务,继续 select() 等待满足唤醒条件的操作(wakeup,事件发生),有任务执行 selectNow() 返回准备好事件的key的个数
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;
                    case SelectStrategy.SELECT:
                        //继续 select()
                        select(wakenUp.getAndSet(false));
                        if (wakenUp.get()) {
                            selector.wakeup();
                        }
                        // fall through
                    default:
                }

                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                //ioRatio 为 100 的时候就执行完 io 事件之后执行所有的 task
                if (ioRatio == 100) {
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        runAllTasks();
                    }
                } else {
                    final long ioStartTime = System.nanoTime();
                    try {
                        //处理有事件发生的 key
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        final long ioTime = System.nanoTime() - ioStartTime;
                        //计算出执行普通任务占的百分比
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
            // Always handle shutdown even if the loop processing threw an exception.
            try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }

核心部分为 processSelectedKeys(); 处理有事件的所有的 key 的processSelectedKeys(); -> processSelectedKeysPlain()
循环处理所有有事件发生的 key

 Iterator<SelectionKey> i = selectedKeys.iterator();
        for (;;) {
            final SelectionKey k = i.next();
            //
            final Object a = k.attachment();
            i.remove();

            if (a instanceof AbstractNioChannel) {
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }

            if (!i.hasNext()) {
                break;
            }
        }

接着看 processSelectedKey() 处理一个 key 的事件。
下面分别有对应的 accept write read 等事件的处理

        try {
            int readyOps = k.readyOps();
            // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
            // the NIO JDK channel implementation may throw a NotYetConnectedException.
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                unsafe.finishConnect();
            }
            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }
            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }

以上就已经基本清楚了, netty 对 io 的操作 nio Selector 编程的几个步骤都是在 EventLoop 这个单线程中完成的。

好了,至于 netty 为什么在其他线程write() 是线程安全的。是怎么实现的,在下节在讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值