EventLoopGroup与EventLoop 源码分析

原创
2018/03/19 18:22
阅读数 3.3K

源码地址: GitHub

1.NioEventLoopGroup实例化过程

下面来分析对NioEventLoopGroup类进行实例化的过程中发生了什么。

NioEventLoopGroup 类层次结构

先给出类图:


 

我们查看上面接口的结构图:

ExecutorService模拟Java线程池接口,ScheduledExecutorService模拟定时器线程池接口。

对于NioEventLoopGroup核心的类继承关系就是:

NioEventLoopGroup –》MultithreadEventLoopGroup –》MultithreadEventExecutorGroup

面从这三个类出发分析NioEventLoopGroup实例化过程。

首先盗用一下网上的一张示意图

下面大致解释一下这个实例化过程做了什么

//对于不指定线程数参数的构造器,默认设置0 (但是在后面的构造器中会判断,如果设置为0 就会初始化为2*CPU)
    public NioEventLoopGroup() {
        this(0);
    }

    /**
     * Create a new instance using the specified number of threads, {@link ThreadFactory} and the
     * {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
     */
//然后调用:这里设置了NioEventLoopGroup线程池中每个线程执行器默认是null(这里设置为null,在后面的构造器中会判断,如果为null就实例化一个线程执行器)
    public NioEventLoopGroup(int nThreads) {
        this(nThreads, (Executor) null);
    }
//再调用:这里就存在于JDK的NIO的交互了,这里设置了线程池的SelectorProvider, 通过SelectorProvider.provider() 返回。
    public NioEventLoopGroup(int nThreads, Executor executor) {
        this(nThreads, executor, SelectorProvider.provider());
    }
//然后调用:在这个重载的构造器中又传入了默认的选择策略工厂DefaultSelectStrategyFactory;
    public NioEventLoopGroup(
            int nThreads, Executor executor, final SelectorProvider selectorProvider) {
        this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
    }
//这里就是调用父类MultithreadEventLoopGroup的构造器了, 这里还添加了线程的拒绝执行策略。
    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }

在MultithreadEventLoopGroup构造器调用:

    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

接下来就是调用的基类MultithreadEventExecutorGroup的构造器:

//这个构造器里面多传入了一个参数 DefaultEventExecutorChooserFactory.INSTANCE ,
// 通过这个EventLoop选择器工厂可以实例化GenericEventExecutorChooser或者PowerOfTwoEventExecutorChooser
//这个类, 这个类是EventLoopGroup线程池里面的EventLoop的选择器,
//调用GenericEventExecutorChooser.next() 方法可以从线程池中选择出一个合适的EventLoop线程。    

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

然后就是重载调用MultithreadEventExecutorGroup类的构造器:

构造器调用到了这里其实也就是不断向上传递调用的终点了,由于构造器代码比较长,我就删除一些校验和不重要的代码,只保留核心代码:

/**
 * 最终的创建实例构造器
 *
 * @param nThreads          该实例将使用的线程数
 * @param executor          将要使用的executor, 默认为null
 * @param chooserFactory    将要使用的EventExecutorChooserFactory
 * @param args              arguments which will passed to each {@link #newChild(Executor, Object...)} call
 */
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    /** 1.初始化线程池 */
    //参数校验nThread合法性,
    if (nThreads <= 0) {
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }
	//executor校验非空, 如果为空就创建ThreadPerTaskExecutor, 该类实现了 Executor接口
    //这个executor 是用来执行线程池中的所有的线程,也就是所有的NioEventLoop,其实从
    //NioEventLoop构造器中也可以知道,NioEventLoop构造器中都传入了executor这个参数。
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
	//这里的children数组, 其实就是线程池的核心实现,线程池中就是通过指定的线程数组来实现线程池;
    //数组中每个元素其实就是一个EventLoop,EventLoop是EventExecutor的子接口。
    children = new EventExecutor[nThreads];

	//for循环实例化children数组,NioEventLoop对象
    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
        	//newChild(executor, args) 函数在NioEventLoopGroup类中实现了, 
            // 实质就是就是存入了一个 NIOEventLoop类实例
            children[i] = newChild(executor, args);
            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;
                    }
                }
            }
        }
    }
	// 2.实例化线程工厂执行器选择器: 根据children获取选择器 
    chooser = chooserFactory.newChooser(children);
	// 3.为每个EventLoop线程添加 线程终止监听器
    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);
    }
	// 4. 将children 添加到对应的set集合中去重, 表示只可读。
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);//(EventLoop是EventExecutor的子接口)
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

总结一下上面的初始化步骤(3)中的一些重点:

1)NIOEventLoopGroup的线程池实现其实就是一个NIOEventLoop数组,一个NIOEventLoop可以理解成就是一个线程。

2)所有的NIOEventLoop线程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是属于某一个NIOEventLoopGroup的。 这一点从 newChild(executor, args); 方法就可以看出:newChild()的实现是在NIOEventLoopGroup中实现的。

    @Override
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

3)当有IO事件来时,需要从线程池中选择一个线程出来执行,这时候的NioEventLoop选择策略是由GenericEventExecutorChooser实现的, 并调用该类的next() 方法获取到下一个 NioEventLoop.

到了这里线程池的初始化就已经结束了, 基本这部分就只涉及 netty线程池的内容,不涉及到channel 与 channelPipeline和ChannelHandler等内容。下面的内容就是分析 NioEventLoop的构造器实现了。

2. NioEventLoop类层次结构

NioEventLoop 的类层次结构图还是比较复杂的, 不过我们只需要关注几个重要的点即可. 首先 NioEventLoop 的继承链如下:

NioEventLoop -> SingleThreadEventLoop -> SingleThreadEventExecutor -> AbstractScheduledEventExecutor

在 AbstractScheduledEventExecutor 中, Netty 实现了 NioEventLoop 的 schedule 功能, 即我们可以通过调用一个 NioEventLoop 实例的 schedule()方法来运行一些定时任务. 而在 SingleThreadEventLoop 中, 又实现了任务队列的功能, 通过它, 我们可以调用一个 NioEventLoop 实例的 execute() 方法来向任务队列中添加一个 task, 并由 NioEventLoop 进行调度执行.

通常来说, NioEventLoop 肩负着两种任务,: 
1)第一个是作为 IO 线程, 执行与 Channel 相关的 IO 操作, 包括 调用 select 等待就绪的 IO 事件、读写数据与数据的处理等;

2)而第二个任务是作为任务队列, 执行 taskQueue 中的任务, 例如用户调用 eventLoop.schedule 提交的定时任务也是这个线程执行的.

NioEventLoop 的实例化过程

对于NioEventLoop的实例化,基本就是在NioEventLoopGroup.newChild() 中调用的,下面先给出源码:

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

从上面函数里面 new NioEventLoop()出发分析实例化过程:

(1)最先调用NioEventLoop 里面的构造函数:

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        if (strategy == null) {
            throw new NullPointerException("selectStrategy");
        }
        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

需要注意的是:构造器里面传入了 NioEventLoopGroup、Executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler。从这里可以看出,一个NioEventLoop属于某一个NioEventLoopGroup, 且处于同一个NioEventLoopGroup下的所有NioEventLoop 公用Executor、SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler。

还有一点需要注意的是,这里的SelectorProvider构造参数传入的是通过在NioEventLoopGroup里面的构造器里面的 SelectorProvider.provider();方式获取的, 而这个方法返回的是一个单例SelectorProvider, 所以所有的NioEventLoop公用同一个单例SelectorProvider。

 

(2)核心的东西说完了,就是调用父类SingleThreadEventLoop的构造器:

    protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
        //父类构造器
        super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
        tailTasks = newTaskQueue(maxPendingTasks);
    }

这里除了调用父类SingleThreadEventExecutor的构造器以外, 就是实例化了 tailTasks 这个变量; 
对于tailTasks在SingleThreadEventLoop属性的定义如下:

private final Queue<Runnable> tailTasks;// 尾部任务队列

队列的数量maxPendingTasks参数默认是SingleThreadEventLoop.DEFAULT_MAX_PENDING_TASK,其实就是Integer.MAX_VALUE; 对于new的这个队列, 其实就是一个LinkedBlockingQueue 无界队列。

(3)再看调用的父类SingleThreadEventExecutor的构造器:

    protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, int maxPendingTasks,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);// 设置EventLoop所属于的EventLoopGroup
        this.addTaskWakesUp = addTaskWakesUp;
        this.maxPendingTasks = Math.max(16, maxPendingTasks);//默认是Integer.MAX_VALUE
        this.executor = ObjectUtil.checkNotNull(executor, "executor");
        taskQueue = newTaskQueue(this.maxPendingTasks);//创建EventLoop的任务队列, 默认是 LinkedBlockingQueue
        rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }

自此,NioEventLoop的实例化过程已经分析完毕。

3接下来分析ServerBootstrap 的引导过程

ServerBootstrap的继承结构图如下: ServerBootstrap中变量负责SocketChannel的生成,AbstractBootstrap负责ServerSocketChannel的生成。

服务端程序代码如下:(不重要的部分已经省略)

public class BaseServer {
...
    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap sbs = new ServerBootstrap()
                    .group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));

                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new BaseServerHandler());
                        };

                    }).option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            // 绑定端口,开始接收进来的连接
            ChannelFuture future = sbs.bind(port).sync();

            System.out.println("Server start listen at " + port );
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
...
}

这里调用时,我们传入了两个EventLoopGroup分别叫bossGroup和childGroup。boss这个EventLoopGroup作为一个acceptor负责接收来自客户端的请求,然后分发给worker这个EventLoopGroup来处理所有的时间event和channel的IO.

3.1设置EventLoopGroup对象

    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;
    }

查看上面源码可知,首先调用super.group() 这个方法调用ServerBootstrap的父类AbstractBootstrap的group(EventLoopGroup group) 方法,下面看看AbstractBootstrap方法的定义:

    public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return (B) this;
    }

3.2 设置ServerBootstrap的channel() : channel(NioServerSocketChannel.class)

实际上是调用的是AbstractBootstrap里面的channel()函数,下面先粘贴出该方法的源码:

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

这里传入的是一个Class对象,根据传入的不同的Class对象,实例化不同的Channel,主要是有两种代表NIO和OIO的对象:NioServerSocketChannel和OioServerSocketChannel。在函数体的最后调用了: 

channelFactory(new ReflectiveChannelFactory<C>(channelClass));

参数里面实例化了一个 ReflectiveChannelFactory对象,这个对象实现了ChannelFactory这个接口的。接下来进入这个函数

    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return (B) this;
    }

最后把new的 ReflectiveChannelFactory传递给了AbstractBootstrap的channelFactory属性,该属性定义如下:

private volatile ChannelFactory<? extends C> channelFactory;

3.3 接下来配置ServerBootstrap的childHandler(ChannelHandler childHandler)

该函数的主要作用是设置channelHandler来处理客户端的请求的channel的IO,这里我们一般都用ChannelInitializer这个类的实例或则继承自这个类的实例,我们实现如下:

childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));

        ch.pipeline().addLast(new StringDecoder());
        ch.pipeline().addLast(new BaseServerHandler());
    };
})

我们ChannelInitializer这个类的继承图:

ChannelInitializer继承自ChannelHandler。 并且这个类其实就是往pipeline中添加了很多的channelHandler,在ServerBootstrap.childHandler(ChannelHandler childHandler)中的实现如下:传入的childHandler赋值给ServerBootstrap的childHandler属性。

3.4 接下来配置ServerBootstrap的option(ChannelOption option , T value);

这里调用父类AbstractBootstrap的option()方法,源码如下:

public <T> B option(ChannelOption<T> option, T value) {
    if (option == null) {
        throw new NullPointerException("option");
    }
    if (value == null) {
        synchronized (options) {
            options.remove(option);
        }
    } else {
        synchronized (options) {
            options.put(option, value);
        }
    }
    return (B) this;
}

重要代码是options.put(option,value); 这里用到了options这个参数,在AbstractBootstrap的定义如下:

private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();

可知是私有变量,而且是一个Map集合,这个变量主要是设置TCP链接中的一些可选项,而且这些属性是作用于每一个连接到服务器被创建的channel.

3.5 然后调用ServerBootstrap的childOption(ChannelOption childOption,T value);源码如下:

public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
    if (childOption == null) {
        throw new NullPointerException("childOption");
    }
    if (value == null) {
        synchronized (childOptions) {
            childOptions.remove(childOption);
        }
    } else {
        synchronized (childOptions) {
            childOptions.put(childOption, value);
        }
    }
    return this;
}

这个函数和option()函数类似,唯一区别是该属性设定只作用于被acceptor(也就是boss EventLoopGroup)接收之后的channel。

3.6 设置ServerBootstrap的绑定端口和启动:bind(int port)

 

 

 

 

 

前面已经分析完了EventLoopGroup和EventLoop,那么有一个问题,我们知道一个EventLoop实际上是对应于一个线程,那么这个EventLoop是什么时候启动的呢?

//未完待续

 

 

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
6 收藏
0
分享
返回顶部
顶部