netty之NioEventLoop事件循环处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了netty之NioEventLoop事件循环处理相关的知识,希望对你有一定的参考价值。

参考技术A NioEventLoop的事件循环处理,就是在一个死循环中处理IO事件和队列里的任务,并且可以根据策略来平衡这两者之间的执行比例。

首先,先来看下selectStrategy,netty中只有一个默认实现

这个策略,若是当前有任务,那么返回selectNow()方法的返回值,若是没有任务,则返回SelectStrategy.SELECT(-1)。

因此接下来的swtich语句块中只会有一种情况,就是值为-1时,表示没有任务。但是并不是就进入无限的阻塞状态select()方法中,还会判断队列是否有定时任务要执行,若有,则计算到下一次定时任务的时间间隔,并传给select()方法中,表示超时时间,这个是为了防止一直在select等待,而没有及时的执行定时任务。

这个超时时间还会设置到原子变量nextWakeupNanos中,这样应用程序就可以通过nextWakeupNanos获取到下一次线程唤醒的时间。当线程唤醒后,程序finally会执行nextWakeupNanos.lazySet(AWAKE),表示线程目前是唤醒状态。这个变量的主要作用是当线程阻塞在select方法时,而此时又有任务提交给这个NioEventLoop执行时

唤醒selector时,会先判断inEventLoop,因为若是inEventLoop,就是目前的任务正在被NioEventLoop的线程执行,并没有阻塞在selector的select方法,还有会对nextWakeupNanos的值设置为AWAKE唤醒状态,若该变量值之前就是唤醒的,那么也不会唤醒selector。

现在,把流程又回到刚刚的事件循环run方法中,当select方法返回后,要执行selectKeys和任务时,会先判断ioRatio这个参数,这个表示的是在当前循环中处理IO事件的时间与任务的比例

在每次的循环最后,会判断NioEventLoop是否shutdown了,若关闭了,则将Selector上的key都cancel,并关闭channel。

netty源码之接收连接

目录

 

接收链接

netty的接收连接

前话

1、bossGroup 轮询链接事件

2、bossGroup 创建socketChannel

3、ServerBootstrapAcceptor注册到worker线程

4、workerGroup 将 socketChannel 注册到选择的NioEventLoop的selector

5、workerGroup 注册读事件


接收链接

NIO的读事件

 while(!stop){//循环遍历selector,休眠时间为1S,当又处于就绪状态的CHannel时,selector将返回该channel的集合。通过对Channel集合的迭代,可进行网络异步读写操作
        try {
            selector.select(1000);
            Set<SelectionKey> selectKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectKeys.iterator();
            SelectionKey key = null;
            while (it.hasNext()){
                key = it.next();
                it.remove();
                handleInput(key);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

netty的接收连接

前话

轮询连接和读事件都是在NioEventLoop对象里。这里有一个run方法。

1、bossGroup 轮询链接事件

//NioEventLoop  ,不管是Boss 还是 Worker都是在这里监听
protected void run() {
    for (;;) {
        try {
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:
                    continue;
                case SelectStrategy.SELECT:
                    //这里是在轮询selector 等待事件 
                    //这里是在轮询selector 等待事件
                    select(wakenUp.getAndSet(false));
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                default:
            }
​
            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio;
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();
                } finally {
                    runAllTasks();
                }
            } else {
                final long ioStartTime = System.nanoTime();
                try {
                    //处理刚才监听到的事件
                    processSelectedKeys();
                } finally {
                    final long ioTime = System.nanoTime() - ioStartTime;
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
        try {
            if (isShuttingDown()) {
                closeAll();
                if (confirmShutdown()) {
                    return;
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
    }
}
​
// 处理selectedKeys
private void processSelectedKeys() {
        if (selectedKeys != null) {
            //这个是优化的方法 , 据说性能提高2%
            processSelectedKeysOptimized();
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
 }
​
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            final EventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {
                return;
            }
            //channel绑定的线程不是自己,那么不做处理,可以看到,这是线程安全的。
            if (eventLoop != this || eventLoop == null) {
                return;
            }
            // close the channel if the key is not valid anymore
            unsafe.close(unsafe.voidPromise());
            return;
        }
​
        try {
            int readyOps = k.readyOps();
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                unsafe.finishConnect();
            }
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                ch.unsafe().forceFlush();
            }
            //如果是链接或者读事件
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            //注意 如果是链接的事件,走的是 AbstractNioMessageChannel.read
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

2、bossGroup 创建socketChannel

// 接收一个nio的 socketChannel , 并将其封装成NioSocketChannel , 并设置为OP_READ

//注意 如果是链接的事件,走的是 AbstractNioMessageChannel.read

//AbstractNioMessageChannel.NioMessageUnsafe.read
 public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);
​
            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        //在这里是接收了一个socketChannel 并加入到buf 
                        // SocketChannel ch = SocketUtils.accept(javaChannel());
                        // buf.add(new NioSocketChannel(this, ch));
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }
​
                        allocHandle.incMessagesRead(localRead);
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }
                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    //在这里触发 注册的
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();
​
                if (exception != null) {
                    closed = closeOnReadError(exception);
​
                    pipeline.fireExceptionCaught(exception);
                }
​
                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        } 

3、ServerBootstrapAcceptor注册到worker线程

ServerBootstrapAcceptor 这是一个关键的类 , 它将Boss监听到链接事件 ,注册到worker线程池

//ServerBootstrapAcceptor 
public void channelRead(ChannelHandlerContext ctx, Object msg) {
            //这个channel 是 第二步 封装的 NioSocketChannel对象
            final Channel child = (Channel) msg;
            //这是引导程序配置的handlers
            child.pipeline().addLast(childHandler);
            //设置参数
            setChannelOptions(child, childOptions, logger);
​
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
​
            try {
                //选择一个NioEventLoop 注册NioSocketChannel
                //next().register(channel)
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

4、workerGroup 将 socketChannel 注册到选择的NioEventLoop的selector

这块和启动流程的注册一样 ​ 注册一个 0事件(selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);)

5、workerGroup 注册读事件

DefaultChannelPipeline.HeadContext.read() , 在这里注册时间

以上是关于netty之NioEventLoop事件循环处理的主要内容,如果未能解决你的问题,请参考以下文章

netty源码之接收连接

netty源码之接收连接

netty源码之接收连接

7.提交任务到 NioEventLoop(Nio事件循环执行线程)

构建一个基于消息驱动的Eventloop线程模型

想统计Netty的一轮事件循环的耗时,该怎么做?