• netty源代码分析笔记--NioEventLoop的创建和启用


    NioEventLoop的创建

    new NioEventLoopGroup() ->

    MultithreadEventExecutorGroup.MultithreadEventExecutorGroup()

    • EventLoopGroup(其实是MultithreadEventExecutorGroup) 内部维护一个类型为 EventExecutor children 数组, 其大小是 nThreads, 这样就构成了一个线程池, Netty 的 EventLoopGroup 的实现机制其实就建立在 MultithreadEventExecutorGroup 之上. 每当 Netty 需要一个 EventLoop 时, 会调用 next() 方法获取一个可用的 EventLoop

    • 如果我们在实例化 NioEventLoopGroup 时, 如果指定线程池大小, 则 nThreads 就是指定的值, 反之是处理器核心数 * 2

    • MultithreadEventExecutorGroup 中会调用 newChild 抽象方法来初始化 children 数组

    • 抽象方法 newChild 是在 NioEventLoopGroup 中实现的, 它返回一个 NioEventLoop 实例.

    • NioEventLoop 属性:

      • SelectorProvider provider 属性: NioEventLoopGroup 构造器中通过 SelectorProvider.provider() 获取一个 SelectorProvider

      • Selector selector 属性: NioEventLoop 构造器中通过调用通过 selector = provider.openSelector() 获取一个 selector 对象.

    • 创建一个线程选择器chooser,作用是从children线程组中,选取一个线程。
    protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                                EventExecutorChooserFactory chooserFactory, Object... args) {
        //nThreads个数默认是cpu核数的两倍
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
        //线程创建器,里面主要是execute方法<br>        
        //execute方法为threadFactory.newThread(command).start(); 每次执行任务的时候,都会创建一个线程
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
    
        children = new EventExecutor[nThreads];
    
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                //详见下面newChild的分析
    //数组中每个元素其实就是一个EventLoop,EventLoop是EventExecutor的子接口。 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 = 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); } //将children线程组,存放到一个不可变的集合中,作为只读Set Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); }

      

    newChild()

    • 保存线程创建器ThreadPerTaskExecutor
    • 创建一个TaskQueue
    • 创建一个selector

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

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

    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }
    
    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                     SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        //调用父类构造器去保存线程创建器和创建TaskQueue
        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;
        selector = openSelector();
        selectStrategy = strategy;
    }
    

      

    taskQueue

    protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
            return new LinkedBlockingQueue<Runnable>(maxPendingTasks);
        }
    

    NioEventLoop 继承于 SingleThreadEventLoop, 而 SingleThreadEventLoop 又继承于 SingleThreadEventExecutor. SingleThreadEventExecutor 是 Netty 中对本地线程的抽象, 它内部有一个 Thread thread 属性, 存储了一个本地 Java 线程. 因此我们可以认为, 一个 NioEventLoop 其实和一个特定的线程绑定, 并且在其生命周期内, 绑定的线程都不会再改变.

    NioEventLoop -> SingleThreadEventLoop -> SingleThreadEventExecutor -> AbstractScheduledEventExecutor

     

    NioEventLoop的启用

    • 服务端启动绑定端口
    • 新连接接入通过chooser绑定一NioEventLoop

    bind -> execute(task) 入口

         -> startThread() -> doStartThread() 创建线程

            ->ThreadPerTaskExecutor.execute()

                ->NioEventLoop.run() 启动

    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);
        }
    }
    
    
    private void doStartThread() {
        assert thread == null;
        //此处的executor即为ThreadPerTaskExecutor,调用execute则会创建一个新线程
        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;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } 
            }
        });
    }
    
    • NioEventLoop执行

    SingleThreadEventExecutor.this.run()

         ->NioEventLoop.run() -> for(;;)

            select() 检查是否有io事件

            processSelectedKeys() 处理io事件

            runAllTasks() 处理异步任务队列

    for (;;) {
        try {
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:
                    continue;
                case SelectStrategy.SELECT:
                    //轮询注册在selector上的io事件
                    select(wakenUp.getAndSet(false));
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                default:
                    // fallthrough
            }
    
            cancelledKeys = 0;
            needsToSelectAgain = false;
            //ioRatio如果没有设置的话,默认是50
            final int ioRatio = this.ioRatio;
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();
                } finally {
                    // Ensure we always run tasks.
                    runAllTasks();
                }
            } else {
                final long ioStartTime = System.nanoTime();
                try {
                    processSelectedKeys();
                } finally {
                    // Ensure we always run tasks.
                    final long ioTime = System.nanoTime() - ioStartTime;
                    //此处传进去的是一个处理io的时间
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
    }
    

      

    •  io事件检测,select()方法的执行逻辑

    deadline及任务穿插逻辑处理

    没有任务时,阻塞式select,默认为1s

    避免jdk空轮询bug问题

    总结:

    • 用户代码NioEventLoopGroup() 时,一组NioEventLoop被创建,创建时,每个NioEventLoop会创建一个selector和一个定时任务队列
    • NioEventLoop首次调用execute()时,会创建一个新线程,然后将线程保存到成员变量,这样以后就能判断是否是该线程。
    • NioEventLoop的执行逻辑在run()方法中,主要包括三个过程:检测io事件,处理io事件,执行任务队列。
    • netty外部线程调用execute()方法时,需要通过inEventLoop()方法判断得出是外部线程,则会把操作封装成一个task,丢到任务队列里,然后等到NioEventLoop执行逻辑的第三个过程,这些task被执行。

    参考:

    https://segmentfault.com/a/1190000006824196

    https://segmentfault.com/a/1190000007403873

    http://ifeve.com/selectors/

  • 相关阅读:
    linux线程
    linux线程
    c++之堆、栈、数据段、
    fork()、僵死进程和孤儿进程
    linux之管理mysql
    linux之管理apache
    Django 时间与时区设置问题
    Django rest framework:__str__ returned non-string (type NoneType) 真正原因
    Django获取当前页面的URL——小记
    Django中出现:TemplateDoesNotExist at
  • 原文地址:https://www.cnblogs.com/kangjianrong/p/9233636.html
Copyright © 2020-2023  润新知