Netty 学习 - EventLoop

     上一篇文章记录了服务端和客户端的启动流程,本文介绍一下类关系以及eventLoop等知识,本研究主要还是以NIO为主
   




                                                                                                    AbstractNioByteChannel     -    NioSocketChannel
Channel  -  AbstractChannel  -  AbstractNioChannel - 
                                                                                                    AbstactNioMessageChannel  -  NioServerSocketChannel








                                                                                                                                                                                       
Unsafe(Channel) -  AbstractUnsafe(AbstractChannel) -  AbstractNioUnsafe(AbstractNioChannel ) 
                                                                                          
                                                                                      
             NioByteUnsafe(AbstractNioByteChannel)     -        NioSocketChannelUnsafe(NioSocketChannel)                                         
                                                                                     
  --
             NioMessageUnsafe( AbstactNioMessageChannel )




                                                                                                                                                                            
Unsafe是Channle的内部接口,这么定义的目的是为了独立出unsafe的功能,unsafe是用于真正通信的组件,而unsafe又应该只被channel调用,而不应该被其他模块可见




再看一下ServerBootStrap中的两个group的含义以及目的,第一个group可以称为bossGroup,第二个group可以称为workerGroup,在Server启动后,bossGroup中一个eventLoop会启动进行轮询, NioServerSocketChannel在这个eventLoop的selector上注册了Accept事件,当有客户端连接上来后,会触发selectKey,
在eventLoop中处理selectKey的代码中
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
                if (!ch.isOpen()) {
                    // Connection already closed - no need to handle write.
                    return;
                }
            }








执行到  NioMessageUnsafe的read方法,执行到NioServerSocketChannel的doReadMessages方法
 @Override
    protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = javaChannel().accept();




        try {
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            logger.warn("Failed to create a new channel from an accepted socket.", t);




            try {
                ch.close();
            } catch (Throwable t2) {
                logger.warn("Failed to close a socket.", t2);
            }
        }




        return 0;
    }








  
此时一个新的连接在服务端就会有一个新的NioSocketChannel对象,到这里为止,只是对象建立了,后续还有对象的pipeline,handler等等需要构建




在  NioMessageUnsafe完成  doReadMessages方法后,会触发 pipeline.fireChannelRead(readBuf.get(i)),由于这个pipeline是NioServerSocketChannel的pipeline
readBuf.get(i)的值就是前面新建的NioSocketChannel对象,根据handler链调用到前面一章介绍到的 ServerBootstrapAcceptor,执行到channelRead方法,该方法包括两个主要工作
1    child.pipeline().addLast(childHandler);    该步骤会将应用程序预先设置的的handler加到该NioSocketChannel的pipeline中                                                                                    
                                                                                    
2    childGroup.register(child) ,其中childGroup就是用户设置的workGroup




从上面的解释可以很清楚的看到,处理客户端连接的是bossGroup,而新建NioSocketChannel对象后,这个对象会用workerGroup进行连接处理等




再来看看EventLoop,还是以NioEventLoop为例来介绍,NioEventLoop需要处理网络I/O读写事件,还需要执行系统任务和定时任务


在Netty的很多代码中,都会有如下实现方式:


if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });


首先会判断当前执行线程是否是eventLoop线程,如果不是会通过eventLoop的execute方法进行异步执行,再来看看execute方法


execute方法在SingleThreadEventExecutor
public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }


        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }


如果当前线程是eventLoop线程,则直接放到task队列中,如果不是,则尝试开始启动eventLoop线程并在内部缓存进行记录线程启动状态后放入task队列


eventLoop中有for的循环体,用来处理selectkey事件,eventLoop处理IO和系统任务的比例有ioRadio控制,eventLoop会保证先处理完IO事件,完成IO事件后,会根据IO事件的事件和配置的比例算出对应执行系统任务的时间,开始执行系统任务,执行64个任务后,比较任务总执行时间是否超过配置的时间,再进行对应的处理
 
 // Check timeout every 64 tasks because nanoTime() is relatively expensive.
            // XXX: Hard-coded value - will make it configurable if it is really a problem.
            if ((runTasks & 0x3F) == 0) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                if (lastExecutionTime >= deadline) {
                    break;
                }




最后来看看EventLoopGroup的关闭方法,shudownGraceFully的基本逻辑如下:
每个eventLoop中的都有内部变量STATE_UPDATE用来维护该eventLoop的状态,当执行shutdownGraceFully方法时,会将状态设置为shutdown,在这个步骤中仅仅是设置状态即可
在eventLoop的轮询中,完成IO轮询和任务调度后,会执行以下操作
 
 
if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        break;
                    }
                }






 其中if的判断就是会去检查状态,如果是shutdown,则会执行closeAll,注意是group执行的shutodown,所以group中管理的所有eventLoop均会轮询到closeAll方法中


closeAll会循环eventLoop上的多路复用器selector上绑定的所有channel,通过channel的unsafe依次关闭channel,释放线程池,完成关闭操作


如果是用户调用的channel.close则会封装为用户任务,在eventLoop的轮询中进行处理




对于任何架构,线程模型设计的好坏都直接影响软件的性能和并发处理能力


            
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭