netty探索之旅七

前面我们分析了Pipeline,还有一个东西值得我们去研究研究,那就是EventLoop。哈哈!我们继续吧!

不管是在客户端还是服务端,在netty启动的时候,都会指定EventLoopGroup,当然用的最多的就是NIO,所以都会指定NioEventLoopGroup。那么这个NioEventLoopGroup在Nnetty中是扮演什么角色的喃!netty是基于Reactor模型的一个实现,那么我们先简单的说明一下Reactor的线程模型。

单线程模型:
所有的acceptor处理和handler处理都在同一个线程中。这种模式的坏处就是显而易见的了,当一个handler阻塞时,会导致所有的handler都不能执行,同时整个服务也不会接受新请求。

多线程模型:
与单线程模型的区别在于,把acceptor让一个独立的线程来处理,后面有一组NIO的线程池来处理客户端连接的IO操作。
特点:
有一个专门的acceptor线程用于监听客户端的TCP连接请求。
客户端连接的IO操作由一个特定的NIO线程池负责. 每个客户端连接都与一个特定的NIO线程绑定, 因此在这个客户端连接中的所有IO操作都是在同一个线程中完成的.
客户端连接有很多,但是NIO线程池中的线程数是较少的,因此一个NIO线程可以同时绑定到多个客户端连接中。

为什么不让一个特定的线程池来处理acceptor喃???

单线程对应在netty中的表现是什么样子的喃
看一个例子:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup)
 .channel(NioServerSocketChannel.class)
........

NioEventLoopGroup构造参数是1,表明线程池的大小是1,接着调用ServerBootstrap的group(bossGroup)方法。接着我们看看ServerBootstrap的group(bossGroup)方法的实现:
 public ServerBootstrap group(EventLoopGroup group) {
        return group(group, group);
    }

 public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (childGroup == null) {
            throw new NullPointerException("childGroup");
        }
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;
        return this;
    }

很容易我们看出来了,传入一个group的时候,我们的bossGroup和workerGroup是同一个NioEventLoopGroup。并且这个NioEventLoopGroup只有一个线程,这样acceptor和后续的所有客户端连接的IO操作都在这个线程里面了,这个就是对应Reactor的单线程模型。

再看一个例子:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
........

bossGroup只有一个线程,workerGroup的线程是CPU核心数乘以2。这样就是Reactor的多线程模式。

下面来看看NioEventLoopGroup和NioEventLoop初始化过程:


1,NioEventLoopGroup内部维护一个类型为EventExecutor数组(变量:children),构造了一个线程池
2,调用newChild抽象方法来初始化children数组
3,抽象方法newChild在 NioEventLoopGroup 中实现的,返回一个NioEventLoop实例

NioEventLoop:
继承关系:NioEventLoop-->SingleThreadEventLoop-->SingleThreadEventExecutor-->AbstractScheduledEventExecutor。 SingleThreadEventExecutor是netty中对本地线程的抽象,它内部有一个Thread thread属性, 存储了一个本地Java线程。一个NioEventLoop和一个线程绑定.

在AbstractScheduledEventExecutor中,实现了schedule的功能,我们可以通过调用NioEventLoop实例的schedule方法来运行一些定时任务。
在SingleThreadEventLoop中实现了任务队列的功能,我们可以通过调用NioEventLoop实例的execute方法来向任务队列中添加一个task,由NioEventLoop进行调度执行。
NioEventLoop肩负着两种任务:第一作为IO线程,执行与channel的IO操作,包括调用select等待就绪的IO事件,读写数据,数据的处理。第二作为任务队列,执行队列中的任务


关于channel和EventLoop是怎么关联起来的可以看看
http://jishuaige.iteye.com/blog/2356798---netty探索之旅三

EventLoop的启动
NioEventLoop的启动,其实就是NioEventLoop所绑定的本地Java线程的启动。
在NioEventLoop的父类SingleThreadEventExecutor中
private void startThread() {
        if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                thread.start();
            }
        }
    }

这个方法调用NioEventLoop所绑定的本地Java线程的start方法,启动线程。
跟着这个方法被调用轨迹走:SingleThreadEventExecutor中的execute
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();
            }
        }

        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }

在第三节我们分析channel注册的时候,在AbstractUnsafe.register有这段代码:
if (eventLoop.inEventLoop()) {  
                register0(promise);  
            } else {  
                try {  
                    eventLoop.execute(new Runnable() {  
                        @Override  
                        public void run() {  
                            register0(promise);  
                        }  
                    }); 
.....................

eventLoop.execute就是在调用上面的SingleThreadEventExecutor.execute,这样NioEventLoop所对应的线程就启动了。

当EventLoop.execute第一次被调用时,就会触发startThread()的调用,进而EventLoop所对应的Java线程的启动。

猜你喜欢

转载自jishuaige.iteye.com/blog/2360363
今日推荐