切片Netty-SingleThreadEventExecutor

Netty源码分析基于主流的Netty4.x版本。
之前文章中经常出现如下代码

channel.eventLoop().execute(()->{})

本文就来分析下,execute都做了哪些操作。

   public void execute(Runnable task) {
        ...
        // 当前线程是否是eventLoop关联的线程
        // 这里返回false,可能是线程不一致或者是EventLoop还未关联线程
        boolean inEventLoop = inEventLoop();
        // 将任务添加到taskQueue中
        addTask(task);
        if (!inEventLoop) {
        	// 启动一个线程
            startThread();
            // 线程池异常的处理
            ...
        }
		...
    }

一.startThread

	private void startThread() {
		// 校验当前线程池状态是否还未启动
        if (state == ST_NOT_STARTED) {
        	// CAS 比较更新当前为已启动状态
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                 doStartThread();
                 ...
            }
        }
    }

更新完线程池状态,进入实际启动线程的操作

	private void doStartThread() {
        assert thread == null;
        // 这里的executor指的是ThreadPerTaskExecutor
        executor.execute(new Runnable() {
            @Override
            public void run() {
            	// 获取当前线程
                thread = Thread.currentThread();
                ...
                // 更新最近执行任务时间
                updateLastExecutionTime();
                try {
                	// 这里执行NioEventLoop的run方法,是个死循环
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } 
                ...  
            }
        });
    }

这里的executor实际是ThreadPerTaskExecutor,是什么时候设置的呢?
还记得之前的分析NioEventLoopGroup的文章中,如下片段

 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
  		...
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        children = new EventExecutor[nThreads];
        for (int i = 0; i < nThreads; i ++) {
            ...
            // 将实例的executor传入NioEventLoop 
            children[i] = newChild(executor, args);
        }
        ...  
   }           

二.run

进入NioEventLoop的run方法,咋一看代码好多,主要做了如下几件事:

  1. selelct/selectNow: 获取channel事件
  2. processSelectedKeys: 处理channel事件
  3. runAllTasks:执行队列中的任务
protected void run() {
        for (;;) {
            try {
                try {
                	// 获取选择策略
                	// hasTasks是判断taskQueue或者tailTasks中的是否任务
                    switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;
                    case SelectStrategy.BUSY_WAIT:
                    case SelectStrategy.SELECT:
                    	// 堵塞轮训channel事件
                        select(wakenUp.getAndSet(false));
                        if (wakenUp.get()) {
                        	//唤醒正在堵塞的selector
                            selector.wakeup();
                        }
                    default:
                    }
                } 
                cancelledKeys = 0;
                needsToSelectAgain = false;
                // IO操作与任务执行时间比例
                final int ioRatio = this.ioRatio;
                // 这里的IO比例,100表示执行所有任务,否则给一个超时时间
                if (ioRatio == 100) {
                    try {
                    	// 处理channel轮训到的事件 
                        processSelectedKeys();
                    } finally {
                    	// 运行任务
                        runAllTasks();
                    }
                } else {
                    final long ioStartTime = System.nanoTime();
                    try {
                    	// 处理channel轮训到的事件 
                        processSelectedKeys();
                    } finally {
                    	// 计算能执行的时长
                        final long ioTime = System.nanoTime() - ioStartTime;
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } 
            ...
        }
    }

calculateStrategy: 当前有任务,则立即调用selectNow获取准备好IO操作的channel数,否则返回SelectStrategy.SELECT

    public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
        return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;
    }

三. select

private void select(boolean oldWakenUp) throws IOException {
        Selector selector = this.selector;
        try {
            int selectCnt = 0;
            long currentTimeNanos = System.nanoTime();
            // delayNanos获取scheduledTaskQueue是否有到期任务,有的话返回0,否则最接近的到期时间
            long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);

            for (;;) {
                long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
                // 这里说明有到期定时任务
                if (timeoutMillis <= 0) {
                    if (selectCnt == 0) {
                        selector.selectNow();
                        selectCnt = 1;
                    }
                    break;
                }
                // 有任务 && CAS更新wakenUp 成功
                if (hasTasks() && wakenUp.compareAndSet(false, true)) {
                    selector.selectNow();
                    selectCnt = 1;
                    break;
                }
				// 堵塞一定时间,获取channel事件
                int selectedKeys = selector.select(timeoutMillis);
                selectCnt ++;
				// 有任务可以处理,退出循环
                if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
                    break;
                }
                // 线程中断退出循环
                if (Thread.interrupted()) {
                    ...
                    break;
                }

                long time = System.nanoTime();
				// 表示上次select到现在过的时间,是否确实阻塞了timeoutMillis
                if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
                    selectCnt = 1;
                }
                // 如果select没有堵塞这么久,说明发生了空轮训
                else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
                        selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
                    // 空轮训次数超出SELECTOR_AUTO_REBUILD_THRESHOLD,则绑定一个新的selector
                    selector = selectRebuildSelector(selectCnt);
                    selectCnt = 1;
                    break;
                }

                currentTimeNanos = time;
            }
			...
        } 
        ...
    }

四.processSelectedKeys

之前的NioEventLoopGroup的文章中,提到对selector优化,这里就是体现的地方。

	private void processSelectedKeys() {
        if (selectedKeys != null) {
        	// 优化是从数组取值
            processSelectedKeysOptimized();
        } else {
        	// 不优化是从set中获取值
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }

执行优化的方法

private void processSelectedKeysOptimized() {
        for (int i = 0; i < selectedKeys.size; ++i) {
        	// 这里使用优化为数组结构,快速获取值
            final SelectionKey k = selectedKeys.keys[i];
			// 便于GC
            selectedKeys.keys[i] = null;
            final Object a = k.attachment();
            if (a instanceof AbstractNioChannel) {
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }
			...
        }
    }
 private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        ...
        try {
        	// 当前事件
            int readyOps = k.readyOps();
            // 连接事件
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                // 移除感兴趣OP_CONNECT事件
                // 一个channel只有首次连接需要处理,不移除后续select会直接返回不堵塞
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                unsafe.finishConnect();
            }
             // 写事件
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                ch.unsafe().forceFlush();
            }
            // 读事件、接收事件
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } 
          ...
    }

这里看下接收事件,如何体现主从Reactor模型。
read最终会调用 NioMessageUnsafe的read方法

    public void read() {
            ...
            try {
                try {
                    do {
                    	// NioSocketChannel 包装readBuf
                        int localRead = doReadMessages(readBuf);
                        ,..
                    } while (allocHandle.continueReading());
                } 
                ...
                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    // 触发读事件通知 
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                ...	
            } 
            ...
        }

进入NioServerSocketChannel的doReadMessages,这里会包装为NioSocketChannel

protected int doReadMessages(List<Object> buf) throws Exception {
		// 获取新连接
        SocketChannel ch = SocketUtils.accept(javaChannel());
        try {
            if (ch != null) {
            	// 将新连接封装为Netty的NioSocketChannel
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
           ...
        }
        return 0;
    }

fireChannelRead 会发布读事件通知,这时之前设置的ServerBootstrapAcceptor会触发channelRead。
这个方法会将channel注册到workGroup中去处理。

 public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ...
            try {
            	// 注册channel到childGroup,实现了Acceptor接收连接,workGroup处理IO的过程
                childGroup.register(child).addListener(()->{});
            } catch (Throwable t) {
               ...
            }
        }

回顾下接收事件触发流程:

  1. 包装新连接为NioSocketChannel
  2. 发布读事件通知
  3. ServerBootstrapAcceptor接收到读事件,将channel注册到workGroup中

五.runAllTasks

protected boolean runAllTasks(long timeoutNanos) {
		// 将定时任务添加到taskQueue
        fetchFromScheduledTaskQueue();
        // 获取一个任务
        Runnable task = pollTask();
        if (task == null) {
        	// 执行tailTasks中的任务
            afterRunningAllTasks();
            return false;
        }
        // 截止时间,ScheduledFutureTask.nanoTime()获取当前时间
        final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
        // 记录执行的任务数
        long runTasks = 0;
        long lastExecutionTime;
        for (;;) {
            safeExecute(task);
            runTasks ++;
           	// 每64个任务检查一次超时
            if ((runTasks & 0x3F) == 0) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                // 超时接收
                if (lastExecutionTime >= deadline) {
                    break;
                }
            }
			// 获取下一个任务
            task = pollTask();
            if (task == null) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                break;
            }
        }
		// 执行tailTasks中的任务
        afterRunningAllTasks();
        this.lastExecutionTime = lastExecutionTime;
        return true;
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值