阅读 127

webIM系列之三——NioEventLoopGroup线程池

上一篇介绍了Netty的Reactor模型,这一篇,聊一聊Netty中的线程池模型——NioEventLoopGroup。

先上NioEventLoopGroup的类图: 可以说NioEventLoopGroup类的继承体系还是很复杂的,我们需要搞清楚的事情是,NioEventLoopGroup是MultithreadEventLoopGroup的子类,而MultithreadEventLoopGroup的父类是MultithreadEventExecutorGroup,再往上看它的父类是ExecutorService,ExecutorService是一个线程池啊。

在上一篇中,启动一个NettyServer,我们首先创建了两个NioEventLoopGroup,那就先看一下NioEventLoopGroup的初始化过程都做哪些工作。

在NioEventLoopGroup的构造器中,通过调用父类构造器,最终在MultithreadEventExecutorGroup中,创建了Executor以及一个名为children的EventExecutor数组,数组大小为nThreads。

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
        //创建执行器,eventLoopGroup中的executor,这个executor会为每一个channel创建一个线程执行对应的任务,后面还会看到,eventLoopGroup中的每个eventLoop,也包含一个executor
        if (executor == null) {//NioEventLoopGroup构造器中如果定义类ThreadFactory,则这里就不会再执行了,会在子类直接创建executor,当前区别是定义了线程名字
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        //eventLoopGroup中包含一个eventExecutor的数组,数组里面是NioEventLoop的实例,每个NioEventLoop中包含一个executor,一个selector
        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                children[i] = newChild(executor, args); //创建NioEventLoop
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        chooser = chooserFactory.newChooser(children);

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }
复制代码

NioEventLoopGroup的构造器,可以指定线程数,还可以指定线程工厂,为线程池起个名字,上一篇中,我们就是通过指定的线程工厂为bossGroup和workerGroup指定了线程池名称,这样也方便我们通过jvisualvm来查看不同线程池的线程数。

如果不指定线程数量,Netty会使用服务器逻辑内核数的两倍作为默认线程数.

通过NioEventLoopGroup#newChild()方法,创建NioEventLoop的数组,这里我们发现一个很有意思的情况,NioEventLoopGroup是executor,NioEventLoop也是executor,你心里是否有一堆问号?看一下NioEventLoop的类图: 看一下NioEventLoop的构造器:

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
                 EventLoopTaskQueueFactory queueFactory) {
        super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
                rejectedExecutionHandler);
        this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
        this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
        final SelectorTuple selectorTuple = openSelector();
        this.selector = selectorTuple.selector;
        this.unwrappedSelector = selectorTuple.unwrappedSelector;
    }
复制代码

一个NioEventLoopGroup中包含一个executor,一个NioEventLoop的数组;一个NioEventLoop中包含一个selector,一个provider,这两个都是JDK的NIO中提供的,还有一个taskQueue;NioEventLoopGroup和NioEventLoop本身也都是executor。OMG!!!

SingleThreadEventExecutor内部有一个thread,这其实是Netty对本地线程的一种封装,我们可以认为,一个NioEventLoop与一个线程绑定,并且在NioEventLoop的生命周期内,绑定的thread都不会改变。

这一篇就先简单的介绍到这里,下一篇我们具体看看NioEventLoopGroup线程池,如何处理客户端请求。

文章分类
后端
文章标签