Java读源码之Netty深入剖析----4.NioEventLoop

Java读源码之Netty深入剖析----4.NioEventLoop

分析Netty reactor线程处理过程,包括事件监听,事件处理,常规任务处理和定时任务处理


4-1 NioEventLoop概述
4-2 NioEventLoop创建概述
4-3 ThreadPerTaskThread
4-4 创建NioEventLoop线程
4-5 创建线程选择器
4-6 NioEventLoop的启动
4-7 NioEventLoop执行概述
4-8 检测IO事件
4-9 处理IO事件
4-10 -reactor线程任务的执行
4-11 -NioEventLoop总结

 

netty源码阅读之NioEventLoop

 

 

初始阅读源码的时候,晦涩难懂,枯燥无味,一段时间之后就会觉得豁然开朗,被源码的魅力深深折服。

接下去要阅读的是netty的一个重要组件,NioEventLoop。

将会分为以下几点分析。

一、NioEventLoop源码

1、NioEventLoop创建

2、NioEventLoop启动

3、NioEventLoop执行逻辑

二、回答如下问题:

1、默认情况下,netty服务器启动多少个线程?何时启动?

2、netty是如何解决jdk空轮询的bug的?

3、netty如何保证异步串行无锁化?

 

-------------------------------------------------------------------------------------------

 

netty源码阅读之NioEventLoop之NioEventLoop创建


从new NioEventLoopGroup()进入分析NioEventLoop创建,创建分为以下几个过程:

1、创建线程创建器:new ThreadPerTaskExecutor()

2、构造NioEventLoop:for{newChild()}

3、创建线程选择器:chooserFactory.newChooser()

从new 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 ++) {
                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 {
                    ...
            }
     
            chooser = chooserFactory.newChooser(children);
            ...
        }

 这就是刚刚说的三个过程,我们一步步分析

 
一、创建线程创建器:new ThreadPerTaskExecutor()

1、每次执行任务都会创建一个线程实体

2、NioEventLoop线程的命名规则nioEventLoop-(第几个线程池)-(这个线程池的第几个线程)

 

从new NioEventLoopGroup()进入,一层层进入,看到如下:

       public NioEventLoopGroup(int nThreads, Executor executor) {
            this(nThreads, executor, SelectorProvider.provider());
        }

也就是,每个NioEventLoopGroup都会有一个selector,从这里创建。

 

继续进去,有如下代码:

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

如果没有定义线程数量,也就是为0的时候,就使用DEFAULT_EVENT_LOOP_THREADS,它的定义为:

       private static final int DEFAULT_EVENT_LOOP_THREADS;
     
        static {
            DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                    "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
     
            if (logger.isDebugEnabled()) {
                logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
            }
        }

默认为系统线程数的两倍。

 

然后,继续进入,来到我们最开始分析的:

           if (executor == null) {
                executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
            }

查看这个new ThreadPerTaskExecutor()的定义

    public final class ThreadPerTaskExecutor implements Executor {
        private final ThreadFactory threadFactory;
     
        public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
            if (threadFactory == null) {
                throw new NullPointerException("threadFactory");
            }
            this.threadFactory = threadFactory;
        }
     
        @Override
        public void execute(Runnable command) {
            threadFactory.newThread(command).start();
        }
    }

就是传进去一个ThreadFacotory,通过这个threadFactory产生线程。

回答我们之前的一个问题:NioEventLoop什么时候创建线程,在执行ThreadPerTaskExecutor这个execute方法的时候,把一个Runnable传进去创建线程。也就是每次执行任务的时候,创建一个线程实体。

 

回到newDefaultThreadFactory(),查看实现,可以知道上面一步的threadFactory就是DefaultThreadFactory,在里面,有一个

    public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {
            this(toPoolName(poolType), daemon, priority);
        }

toPoolName(poolType),他的实现就是:

        public static String toPoolName(Class<?> poolType) {
            if (poolType == null) {
                throw new NullPointerException("poolType");
            }
     
            String poolName = StringUtil.simpleClassName(poolType);
            switch (poolName.length()) {
                case 0:
                    return "unknown";
                case 1:
                    return poolName.toLowerCase(Locale.US);
                default:
                    if (Character.isUpperCase(poolName.charAt(0)) && Character.isLowerCase(poolName.charAt(1))) {
                        return Character.toLowerCase(poolName.charAt(0)) + poolName.substring(1);
                    } else {
                        return poolName;
                    }
            }
        }

返回其实就是nioEventLoop,因为poolType是NioEventLoop。

一层层点,查看到另外一个构造函数:

     public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
            if (poolName == null) {
                throw new NullPointerException("poolName");
            }
            if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
                throw new IllegalArgumentException(
                        "priority: " + priority + " (expected: Thread.MIN_PRIORITY <= priority <= Thread.MAX_PRIORITY)");
            }
     
            prefix = poolName + '-' + poolId.incrementAndGet() + '-';
            this.daemon = daemon;
            this.priority = priority;
            this.threadGroup = threadGroup;
        }

添加了两个连接符,并把当前线程池的id获取到并且加一了。所以在这个类实现newThread这里,线程的名称就出来了:

        @Override
        public Thread newThread(Runnable r) {
            Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
            ...
            return t;
        }

至于它的线程,就是这个自定义的FastThreadLocalThread:

     protected Thread newThread(Runnable r, String name) {
            return new FastThreadLocalThread(threadGroup, r, name);
        }

 
二、构造NioEventLoop:for{newChild()}

这一步做了三件事情:

1、保存上面创建的线程执行器ThreadPerTaskExecutor

2、创建一个MpscQueue

3、创建一个selector

首先我们看一个类图:

 

newChild出来的就是NioEventLoop,它继承自SingleThreadEventExecutor:

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

在这里,它把刚刚传经来的executor绑定进去了。

然后,newTaskQueue创建的是mpscQueue:

    @Override
        protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
            // This event loop never calls takeTask()
            return PlatformDependent.newMpscQueue(maxPendingTasks);
        }

看看定义:

       /**
         * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
         * consumer (one thread!).
         */
        public static <T> Queue<T> newMpscQueue(final int maxCapacity) {
            return Mpsc.newMpscQueue(maxCapacity);
        }

也就是,一个消费者多个生产者。

 

另外创造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;
            selector = openSelector();
            selectStrategy = strategy;
        }

 
三、创建线程选择器:chooserFactory.newChooser()

这里面对线程的轮询采用了优化的方式

isPowerOfTwo()判断是否是2的幂

1、是

采用PowerOfTwoEventExecutorChooser(优化点),轮询的方式:

index++&(lenght-1)

2、不是

GenericEventExecutorChooser(),轮询的方式:

abs(index++%length)

直接贴代码看好了:

    public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
     
        public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();
     
        private DefaultEventExecutorChooserFactory() { }
     
        @SuppressWarnings("unchecked")
        @Override
        public EventExecutorChooser newChooser(EventExecutor[] executors) {
            if (isPowerOfTwo(executors.length)) {
                return new PowerOfTowEventExecutorChooser(executors);
            } else {
                return new GenericEventExecutorChooser(ex

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值