小白带你认识netty(一)之NioEventLoopGroup

先来段netty服务器端代码:

        EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workGroup = new NioEventLoopGroup(4);

		try
		{
			
			ServerBootstrap server = new ServerBootstrap();
			server.group(bossGroup, workGroup);
			server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100);
			server.childHandler(new DealNettyServerInitializer());
			

			ChannelFuture future = server.bind(7878).sync();
			future.channel().closeFuture().sync();
		}
		catch(InterruptedException e)
		{
			e.printStackTrace();
		}
		finally
		{
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}

代码的一开始:EventLoopGroup类,是什么东东?一起来看下

cb915ff380ac8f3eba797ad00de111b3c0b.jpg

当我们没有传入参数时,默认调用的是参数为0的构造方法,这个参数是个什么意思,看下带有参数的构造方法。

d608b7893213ffe5d13e1eaff334766f372.jpg现在我们应该知道了 number of threads。我们传入了0,就不创建线程了吗?继续跟进去看看。

f15c0eafde5fedea5754e41c950cff707ac.jpg

bfac5ed9820a0df2fea9e24ee6078a68664.jpg

1e9a755271d45a6f9406adb2edbb2749bff.jpg

24c2eb657750f2104b56a844f9acf7dfa83.jpg

哈,这有个判断,如果传入的nThreads为0,就是默认值。这个默认是多少呢,瞅一眼。。。

d820c13ae9302269701c17cc2008dcfc670.jpg

应该是NettyRuntime.availableProcessors()的两倍,那NettyRuntime.availableProcessors()是多少呢?

84aa49e5f0fc41ec0723f25ff77f9678c14.jpg

afde42b15fb298f4ed8d31e9c8edb26d32c.jpg

哦,原来是CPU的核数呀。。因此证明,当运算是IO密集型时候,建议设置CPU核数的2倍呀。

继续回到

6b2b3dec22605c72064a55834bd7fa81127.jpg

这个方法上,跟进父类的构造方法。

4d86dcd04c4fadb33eaad9fa889f22762c5.jpg

记一下DefaultEventExecutorChooserFactory.INSTANCE这个参数,等下我们看个非常有意思的东西。

/**
     * Create a new instance.
     *
     * @param nThreads          the number of threads that will be used by this instance.
     * @param executor          the Executor to use, or {@code null} if the default should be used.
     * @param chooserFactory    the {@link EventExecutorChooserFactory} to use.
     * @param args              arguments which will passed to each {@link #newChild(Executor, Object...)} call
     */
    protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

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

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

就到了真正的逻辑部分了。代码有点多,分为三个部分:

1、new ThreadPerTaskExecutor(newDefaultThreadFactory());线程执行器的实例化

2、循环构建NioEventLoop实例

3、chooserFactory.newChooser(children);线程选择器。

来,一点一点的分析。

1、new ThreadPerTaskExecutor(newDefaultThreadFactory());线程执行器的实例化

8bc409d0774e804578ed8290a21d589831b.jpg

因为刚开始传入的是null,所以这里会新创建一个ThreadPerTaskExecutor对象,并调用newDefaultThreadFactory()方法作为参数。我们一看,这个方法的名字是xxThreadFactory,猜测可能是和线程池类似,是一个默认的线程工厂类。进入看一下

930801c7b54e4c7ddb6dc8f4de2b4d82d8a.jpg

创建了一个DefaultThreadFactory类,并将该类的class作为参数传入。

看下DefaultThreadFactory类的构造方法。。

b7c0c4f7e2a588945ac6fad8bab81ff6068.jpg

cb3d796cf19041b11c9e3806347aca0ed10.jpg

看下toPoolName()方法,

9f572017c84058b8c04d0299734c90b27e2.jpg

该方法就是将class io.netty.channel.nio.NioEventLoopGroup名字转成nioEventLoopGroup,继续跟进

cb3d796cf19041b11c9e3806347aca0ed10.jpg

bdc23227bf26e497c7114e075ca6db16d5a.jpg

 

697146ee9aefb91bbd6abb6b14ed1205d2c.jpg

我们看到了prefix为nioEventLoopGroup-1-;即为netty使用线程名的前缀。

再瞅下ThreadPerTaskExecutor这个线程执行器类,

24414368e9e71fba361a31783a94dd86ada.jpg

此类就一个方法,execute明显是供外界调用的,看下那个线程工厂的newThread方法。。

9c4b7fcf491e06caea1d6b353e81584e48b.jpg

该方法是创建了一个FastThreadLocalThread的类,这应该是一个线程类,和Thread应该是一样的,线程的名字为prefix+1即为prefix为nioEventLoopGroup-1-1。

9a2803a0bd0fa3d528028a344980360e00b.jpg

果然,该类就是线程类。

总结下,executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());这个代码:

首先创建一个执行线程执行器类,并将默认的线程工厂类注入进去。当线程执行器执行execute方法时,线程工厂就会新建一个FastThreadLocalThread线程类,名字为nioEventLoopGroup-1-x。

2、循环构建NioEventLoop实例

回到

8bc409d0774e804578ed8290a21d589831b.jpg

这个代码上,继续向下看:

78692cce598f8c58c062ec9f95917478dd6.jpg

循环为children数组实例化对象。即children[i] = newChild(executor, args);

452ac1ee43677de79ae037e3bf6b3d85cb4.jpg

实例化的对象为NioEventLoop。PS:突然发现,NioEventLoopGroup是由多个NioEventLoop组成。。。这名子取得真是NB。

瞅下NioEventLoop这个类:

aa2742ee7ceb9caeecb6af0e085574ce7e6.jpg

进入父类的构造方法:

7c17bece5c4477ad17f25353d33297ab640.jpg

继续查看父类的构造方法:

6b4d58eae71d47cfd3c13d03c510f32df39.jpg

看下taskQueue=newTask...方法:

cca3e1bc279642a4698c4fa60f15dd17a04.jpg

该newTaskQueue覆盖了父类的

822bc4805489bb3551f82d9c7f1d748707e.jpg

关于newMpscQueue和LinkedBlockingQueue的比较,请看https://my.oschina.net/hmilyylimh/blog/1787788,这个大牛分析的还是比较透彻的。

所以此处的任务队列是netty做了一次优化。

回到eec90540cc42b5ca84133aa0a857b2583b9.jpg

这个方法,看完父类的构造方法,看下final SelectorTuple selectorTuple = openSelector();,

 private SelectorTuple openSelector() {
        final Selector unwrappedSelector;
        try {
            unwrappedSelector = provider.openSelector();
        } catch (IOException e) {
            throw new ChannelException("failed to open a new selector", e);
        }

        if (DISABLE_KEYSET_OPTIMIZATION) {
            return new SelectorTuple(unwrappedSelector);
        }

        final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

        Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    return Class.forName(
                            "sun.nio.ch.SelectorImpl",
                            false,
                            PlatformDependent.getSystemClassLoader());
                } catch (Throwable cause) {
                    return cause;
                }
            }
        });

        if (!(maybeSelectorImplClass instanceof Class) ||
                // ensure the current selector implementation is what we can instrument.
                !((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
            if (maybeSelectorImplClass instanceof Throwable) {
                Throwable t = (Throwable) maybeSelectorImplClass;
                logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
            }
            return new SelectorTuple(unwrappedSelector);
        }

        final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;

        Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                    Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

                    Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
                    if (cause != null) {
                        return cause;
                    }
                    cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
                    if (cause != null) {
                        return cause;
                    }

                    selectedKeysField.set(unwrappedSelector, selectedKeySet);
                    publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                    return null;
                } catch (NoSuchFieldException e) {
                    return e;
                } catch (IllegalAccessException e) {
                    return e;
                }
            }
        });

        if (maybeException instanceof Exception) {
            selectedKeys = null;
            Exception e = (Exception) maybeException;
            logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
            return new SelectorTuple(unwrappedSelector);
        }
        selectedKeys = selectedKeySet;
        logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
        return new SelectorTuple(unwrappedSelector,
                                 new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
    }

卧槽,这么多,不就是为NioEvenetLoop对象创建一个selector对象么: unwrappedSelector = provider.openSelector();为啥会有这么多内容呢???

耐心往下看一点,

6a70d0bf6331309caad46afe7af5b050a5f.jpg

嗯?这是什么鬼?

c42931c71851bc9bfaf0f6ce60df7dcb385.jpg

哦,原来实现了AbstractSet接口,用数组取代HashSet呀。

继续往下看:

ddf0bc6f3560be5152c7b619323e05a8262.jpg

对sun.nio.ch.SelectorImpl这个类进行反射,获取该类的Class对象。

0a3f0e5243839c27c81bb2d5408e7a449c9.jpg

然后通过反射,将SelectorImpl对象中的selectedKeys和publicSelectedKeys设值为SelectedSelectionKeySet对象。debug下:

每调用反射前:

2ead2184b04381fbaf813c5cf26b778cfe8.jpg

调用反射后:

ebc064f134ae3d1655735ecc0c51a82bbe7.jpg

这个也是netty对selectKey做到一个优化点。

至此,完成了对NioEventLoop的select的绑定。

总结下:

NioEventLoop对象实例化时候,先是绑定线程执行器,然后为NioEventLoop对象创建一个性能比较高的mpsc队列,然后再优化select选择器的selectKeys并绑定到该NioEventLoop对象中。

 

3、chooserFactory.newChooser(children);线程选择器。

回到:

78692cce598f8c58c062ec9f95917478dd6.jpg

中,继续向下看:

c5b84938dfc4c2d8baefffa0a2fac9a93dd.jpg

还记的前面记的DefaultEventExecutorChooserFactory.INSTANCE这个参数么,现在就用到了。

这个就是从NioEventLoop数组中轮询选取一个NioEventLoop对象。

看下这个newChooser方法:

3a9c4728f1f90ca2cc663a0474d4462fc55.jpg

看下isPowerOfTwo:

d2014295e43baf42311f3cc6c7424fed014.jpg

该方法是判断val是否为2的指数倍,即2,4,8,16。。。

好高级的写法呀。

所以如果是2的指数倍,采用PowerOfTwoEventExecutorChooser否则GenericEventExecutorChooser。

看下PowerOfTwoEventExecutorChooserb7c587d6e08b05640f1eb4c22f1c444fcbb.jpg

用&的方式选择下一个值。

看下GenericEventExecutorChooser

a6998b7cd5e413a818222ef37bfc76fd82e.jpg

则是通过取余的方式选取下一个值。

就这么一点,netty也是做了优化,可见netty是有多么优秀。

至此NioEventLoopGroup的实例化分析完毕。。。

转载于:https://my.oschina.net/littlestyle/blog/2877484

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值