yihau

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

先来看NioEventLoopGroup类的继承关系

从EventExecutorGroup开始是netty中实现的。EventExecutorGroup中主要增加了一个方法next(),这个方法主要是按一定的规则返回一个EventExecutor,以及一些关闭executors的方法。顺着往下看,左边的类主要是承担任务执行的主体类,主要看一下MultithreadEventExecutorGroup,这个类中有两个重要的域EventExecutor[]和EventExecutorChooserFactory.EventExecutorChooser,这个数组揭示了EventExecutorGroup和EventExecutor的关系,chooser完成返回EventExecutor的选取规则。右边的EventLoopGroup接口主要增加定义了将channel注册到EventLoop的register()方法。下面开始展开分析。

首先从NioEventLoopGroup的实例化开始。

在前两个类中主要设置了线程组的线程数量,然后主要的参数实例化都是在MultithreadEventExecutorGroup中的,来看MultithreadEventExecutorGroup构造器的代码

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
      //这里的executor起到创建线程的作用,它的execute方法内部就是new Thread().start()
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }

        children = new EventExecutor[nThreads];
    //依次创建EventExecutor,这里newChild方法的具体实现是在NioEventLoopGroup中的
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                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;
                        }
                    }
                }
            }
        }
    //初始化chooser
        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);
                }
            }
        };
    //为每个EventExecutor添加监听器表示终止后要做的操作
        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }
  //记录只读children
        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

总结一下要点1、EventLoopGroup其实就是EventLoop的集合,2、EventLoopGroup中的每个EventLoop构建时所传入的参数都一样。

接下来看NioEventLoop,先上继承关系图

比上面的那个复杂一些,但是总体上还是分主体类完成事件执行机制,实现接口增加EventExecutorGroup和EventExecutor的父子关系,其中EventExecutor中还定义了当前线程是否在事件循环执行中,以及是否是channel所绑定的EventLoop。

先看NioEventLoop的实例化过程

 

首先,父类构造调用一直到SingleThreadEventExecutor,这个类中有几个重要的域,其中thread这个域就是我们netty线程模型中实际工作的thread了,然后还有一个taskQueue<Runnable>这里面用于存放需要执行的业务逻辑,是一个阻塞队列。其构造器代码如下:

 protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, int maxPendingTasks,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);
        this.addTaskWakesUp = addTaskWakesUp;
        this.maxPendingTasks = Math.max(16, maxPendingTasks);
        this.executor = ObjectUtil.checkNotNull(executor, "executor");
        taskQueue = newTaskQueue(this.maxPendingTasks);
        rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }

可以看到这里面并没有初始化Thread域,那么什么时候才会去初始化线程实体,然后start呢,这个稍后再说。回到NioEventLoop中,NioEventLoop中也有一个重要的域,那便是Selector,netty中的channel需要注册到eventLoop中,其实也就是需要注册到EventLoop中的Selector上。

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

这里会将底层的Selector打开。至此所有准备工作都做好了,那么就要等待任务来,开启thread了。

先看SingleThreadEventExecutor中的execute方法

public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
    //inEventLoop方法其实是比较当前线程和this.Thread是否“==”。刚开始this.thread还是NULL,故会执行else分支可以看到else中是一个startThread方法,
boolean inEventLoop = inEventLoop(); if (inEventLoop) { addTask(task); } else { startThread(); addTask(task); if (isShutdown() && removeTask(task)) { reject(); } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); }

我们先往后看是如何开启这个thread的然后再回过来看是谁调用了execute方法。

private void doStartThread() {
//断言thread是null,这是必然的,否则程序一定是出现了问题
assert thread == null;
//这里又是execute方法,那是不是和上一个execute方法相同呢,如果是这样那就成了互调死循环了,其实这里的execute的调用者executor是我们之前在EventLoopGroup中创建的然后
传过来的,追过去可以发现这个executor的实例是
ThreadPerTaskExecutor,它的execute方法中的代码是{threadFactory.newThread(command).start()},
到这里会创建线程并执行传过来的runnable。我们再来看runnable中的逻辑,创建完线程后,首先将创建的线程赋值给当前eventloop的thread域,然后执行run方法,这里run是
抽象方法,实际的实现是在NioEventLoop中的。点过去发现run方法是一个无限循环的方法,至此就开始不停的netty底层的事件处理了。
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}

boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run();
success = true;
}
。。。
}

回过头看是谁调用了SingleThreadEventExecutor中的execute方法,客户端发起的connect和服务端的bind的操作都会引发eventloop的execute方法的执行。

private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        final Channel channel = connectPromise.channel();
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }
private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

至此就完成了这些源码的主要分析。还有点需要注意。

由selector引发的操作,connect、read、write这些是IO事件,其他的是一些系统Task包括定时Task,这些任务都是由同样的NioEventLoop中的Thread来完成的,我们可以通过setIorait来调整io和系统task所占用的时间比。把IO线程和业务线程设计为公用一个thread是为了避免当IO线程和业务线程同时操作网络资源的时候竞争锁。这样分别给各自分出一些时间来执行任务可以实现局部的无锁化。

posted on 2018-02-26 10:50  yihau  阅读(1228)  评论(0编辑  收藏  举报