一. 代码下载

Netty代码下载和编译参考前一篇Netty文章
https://blog.51cto.com/483181/2112163

二. 服务器代码分析

2.1 服务器代码编写

一般Netty服务器端这样编写

    EventLoopGroup bossGroup = new NioEventLoopGroup();   //1. 实例化NioEventLoopGroup对象EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap(); //2. b.group(bossGroup, workerGroup) //3. .channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new FixedLengthFrameDecoder(20));}});ChannelFuture f = b.bind(port).sync(); //4.f.channel().closeFuture().sync();} catch (Exception e) {e.printStackTrace();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}

2.2 NioEventLoopGroup

2.2.1 NioEventLoopGroup继承关系

一步步来看,首先看第一个注释,初始化NioEventLoopGroup对象

EventLoopGroup bossGroup = new NioEventLoopGroup();   //1. 实例化NioEventLoopGroup对象

下图是NioEventLoopGroup的类继承图,包含类成员和方法,比较详细。 这个功能是IntelliJ 自带的。
右击NioEventLoopGroup类名,选择Diagrams->Show Diagram->上面有f,m的按钮,分别对应field和method。

如下:

2.2.2 NioEventLoopGroup构造函数

public NioEventLoopGroup() {this(0);}public NioEventLoopGroup(int nThreads) {this(nThreads, (Executor) null);}public NioEventLoopGroup(int nThreads, Executor executor) {this(nThreads, executor, SelectorProvider.provider());}       public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);}public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());}

我们可以看到几点

  1. NioEventLoopGroup采用的是构造函数重载的方式,以适应不同的初始化场景
  2. Executor传的是null
  3. SelectorProvider用的是SelectorProvider.provider()
  4. 然后把构造好的参数都传给父类MultithreadEventLoopGroup (继承关系可以看上图)

2.2.3 SelectorProvider.provider()

private static SelectorProvider provider = null;public static SelectorProvider provider() {synchronized (lock) {if (provider != null)return provider;return AccessController.doPrivileged(new PrivilegedAction<SelectorProvider>() {public SelectorProvider run() {if (loadProviderFromProperty())return provider;if (loadProviderAsService())return provider;provider = sun.nio.ch.DefaultSelectorProvider.create();return provider;}});}}public class DefaultSelectorProvider {private DefaultSelectorProvider() {}public static SelectorProvider create() {return new KQueueSelectorProvider();}
}public class KQueueSelectorProvider extends SelectorProviderImpl {public KQueueSelectorProvider() {}public AbstractSelector openSelector() throws IOException {return new KQueueSelectorImpl(this);}
}

这段代码我们也可以看到几点:

  1. SelectorProvider provider是一个单例,static类型
  2. SelectorProvider.provider的实现,产生了一个KQueueSelectorProvider
  3. KQueueSelectorProvider的openSelector会生成一个KQueueSelectorImpl

这个先记下来,也许后面分析会有用,继续分析MultithreadEventLoopGroup的构造函数。

2.2.4 MultithreadEventLoopGroup

protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);}private static final int DEFAULT_EVENT_LOOP_THREADS;static {DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));}       

上面这段代码我们可以看到这几点:

  1. 如果我们实例化NioEventLoopGroup没有传入参数,也就是没有nThreads,那么就会采用默认的DEFAULT_EVENT_LOOP_THREADS
    DEFAULT_EVENT_LOOP_THREADS如果没有配置io.netty.eventLoopThreads的话,一般是cpu核数*2
  2. MultithreadEventLoopGroup的实例化方法是继续调用父类的初始化方法。

继续父类MultithreadEventExecutorGroup

2.2.5 MultithreadEventExecutorGroup

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,EventExecutorChooserFactory chooserFactory, Object... args) {...children = new EventExecutor[nThreads]; //1. 实例化children数组for (int i = 0; i < nThreads; i ++) { //2. 循环初始化childrenboolean success = false;try {children[i] = newChild(executor, args);success = true;} catch (Exception e) {throw new IllegalStateException("failed to create a child event loop", e);} finally {...}}chooser = chooserFactory.newChooser(children); //3. 实例化chooserfinal FutureListener<Object> terminationListener = new FutureListener<Object>() {@Overridepublic 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);}

上面这段代码可以从下面几个点分析:

private final EventExecutor[] children;
  1. children - EventExecutor数组,大小是nThreads,线程数目。
  2. newChild初始化
    实例类是NioEventLoopGroup.java,返回NioEventLoop对象

    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的继承关系是这样的,继承于SingleThreadEventLoop,别忘了上面我们看到NioEventLoopGroup继承自MultithreadEventLoopGroup.(看名字是单线程和多线程的区别?)

继续看NioEventLoop的构造函数

2.2.6 NioEventLoop

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);provider = selectorProvider;final SelectorTuple selectorTuple = openSelector();selector = selectorTuple.selector;unwrappedSelector = selectorTuple.unwrappedSelector;selectStrategy = strategy;}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);}...
}

从上面这段代码我们可以看出这几点

  1. NioEventLoop里面保存了SelectorProvider selectorProvider, Selector selector, unwrappedSelector(类型是KQueueSelectorImpl)
  2. selector, unwrappedSelector是通过provider.openSelector()打开的.
    根据2.3段的介绍,provider之前介绍的类型是KQueueSelectorProvider,然后它的openSelector会生成一个KQueueSelectorImpl
    所以provider.openSelector()得到是KQueueSelectorImpl,KQueueSelectorImpl的继承关系如下:

继续往回看,看MultithreadEventExecutorGroup的构造函数。

2.2.7 newChooser

EventExecutorChooserFactory.EventExecutorChooser chooser;protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);}chooser = chooserFactory.newChooser(children);

上面代码我们可以看到:

  1. chooserFactory的类型是DefaultEventExecutorChooserFactory,所以newChooser调用的是DefaultEventExecutorChooserFactory.newChooser方法。
    如下:
public EventExecutorChooser newChooser(EventExecutor[] executors) {if (isPowerOfTwo(executors.length)) {return new PowerOfTwoEventExecutorChooser(executors);} else {return new GenericEventExecutorChooser(executors);}}
  1. 传入的参数是children,也就是NioEventLoop数组
  2. DefaultEventExecutorChooserFactory INSTANCE是一个static final类型对象,也就是一种饿汉式的单例模式,如下:
public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();

继续看newChooser的实现

2.2.8 newChooser

newChooser的代码就不贴了,上面就有,从上面代码可以看到:

  1. isPowerOfTwo是用来判断一个整数是否是2的幂,比如(2,4, 8,16,32等等),它的实现方式如下:
private static boolean isPowerOfTwo(int val) {return (val & -val) == val;}private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {private final AtomicInteger idx = new AtomicInteger();private final EventExecutor[] executors;PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}@Overridepublic EventExecutor next() {return executors[idx.getAndIncrement() & executors.length - 1];}}private static final class GenericEventExecutorChooser implements EventExecutorChooser {private final AtomicInteger idx = new AtomicInteger();private final EventExecutor[] executors;GenericEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}@Overridepublic EventExecutor next() {return executors[Math.abs(idx.getAndIncrement() % executors.length)];}}       

这种实现方法感觉比较优雅和高效,首先拿到-val,也就是val的二进制倒转,然后+1。再做&运算。
大家自己可以拿到数字举个例子,比较巧妙。后续自己写代码可以借鉴,这是读源码的一个好处,可以学习到别人很多优秀的写法。

  1. PowerOfTwoEventExecutorChooser和GenericEventExecutorChooser的不同之处在于next方法的算法不一样,作用都是从NioEventLoop数组里面选出一个NioEventLoop对象来。

但是说实话,我没有想到这两种算法有什么区别,如果谁知道,请告诉我,谢谢。

return executors[idx.getAndIncrement() & executors.length - 1];return executors[Math.abs(idx.getAndIncrement() % executors.length)];

继续往回走,MultithreadEventExecutorGroup的构造函数就基本看完了。

三. 总结

我们来总结下NioEventLoopGroup的实例化过程,可以得到以下几点。

1. NioEventLoopGroup的父类MultithreadEventExecutorGroup包含一个NioEventLoop数组children,数组的大小等于nThreads线程数目。如果没有指定,默认一般是cpu核数 x 2

2. NioEventLoopGroup和NioEventLoop一样都是继承自Executor,但是NioEventLoopGroup又包含多个NioEventLoop(children数组),这种关系有点像android里面ViewGroup和View的关系。或者装饰者模式?

3. NioEventLoopGroup继承自MultithreadEventLoopGroup,而NioEventLoop继承自SingleThreadEventLoop,从名字看,不知道和多线程,单线程有没有关系。

4. MultithreadEventLoopGroup有个chooser,执行next方法的时候,会选择下一个NioEventLoop对象,虽然并不知道两个chooser算法有何区别。

5. NioEventLoopGroup里面重写了newChild方法,里面实例化NioEventLoop。

6. NioEventLoop里面包含了Selector,类型是KQueueSelectorImpl

SelectorProvider provider
SelectStrategy selectStrategy

SelectStrategy这个我们上面我们没有关注,其实它是NioEventLoopGroup构造函数传进去的,如下:

public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);}public final class DefaultSelectStrategyFactory implements SelectStrategyFactory {public static final SelectStrategyFactory INSTANCE = new DefaultSelectStrategyFactory();private DefaultSelectStrategyFactory() { }@Overridepublic SelectStrategy newSelectStrategy() {return DefaultSelectStrategy.INSTANCE;}
}final class DefaultSelectStrategy implements SelectStrategy {static final SelectStrategy INSTANCE = new DefaultSelectStrategy();private DefaultSelectStrategy() { }@Overridepublic int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;}
}

所以SelectStrategy的实现类是DefaultSelectStrategy.

在理清楚NioEventLoopGroup实例化的过程之后,我们下一篇继续按照源代码分析Netty服务器端的源代码。

转载于:https://blog.51cto.com/483181/2118817

56. Netty源代码分析-服务器初始化 NioEventLoopGroup实例化相关推荐

  1. java derby 网络模式,Derby源代码分析 -- 服务器启动

    一直以来都想研究下数据库的实现原理,Derby在我眼里绝对是一个完美的切入点.首先它是100%纯Java实现,对于我这种Java程序员来说简直完美的,不需要去懂C,C++...等等无疑减少了很大的难度 ...

  2. Nouveau源代码分析(三):NVIDIA设备初始化之nouveau_drm_probe

    Nouveau源代码分析(三) 向DRM注冊了Nouveau驱动之后,内核中的PCI模块就会扫描全部没有相应驱动的设备,然后和nouveau_drm_pci_table对比. 对于匹配的设备,PCI模 ...

  3. SDL2源代码分析1:初始化(SDL_Init())

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

  4. 【Netty】使用 Netty 开发 HTTP 服务器

    文章目录 一. HTTP 服务器开发 二. HTTP 服务器代码分析 1 . Netty 开发 HTTP 服务器与 TCP 服务器对比 2 . ChannelInitializer 设置 3 . 自定 ...

  5. 区块链教程Fabric1.0源代码分析scc(系统链码)

    区块链教程Fabric1.0源代码分析scc(系统链码),2018年下半年,区块链行业正逐渐褪去发展之初的浮躁.回归理性,表面上看相关人才需求与身价似乎正在回落.但事实上,正是初期泡沫的渐退,让人们更 ...

  6. CASSINI源代码分析

    CASSINI源代码分析 2004-11-10 http://blog.csdn.net/shanhe/ 为什么要分析CASSINI? Cassini(卡西尼)是asp.net上的一个开源项目.主要给 ...

  7. 【Netty】使用 Netty 开发 HTTP 服务器 ( HTTP 请求过滤 )

    文章目录 一. HTTP 服务器请求过滤功能 1 . HTTP 服务器资源请求过滤 2 . HTTP 请求过滤方法 二. HTTP 服务器 ( 资源过滤 ) 代码实现 1 . 服务器主程序 2 . 服 ...

  8. 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t

    新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

  9. Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重劳动成果] 1 背景 之所以写这一篇博客的原因是由于之前有写过一篇<Android应用setCont ...

最新文章

  1. 有向图——强连通分量
  2. Python 实现程序的单一实例
  3. MySql数据库使用入门
  4. mysql command line client和mysql.exe输入密码后闪退问题解决方法
  5. ubuntu中解压rar文件遇到乱码的解决方法
  6. 管理者必看!深度剖析BI与数据仓库,企业能否成功转型就看它
  7. 弃用数据库自增ID,曝光一下我自己用到的解决方法之---终结篇
  8. leetcode—22.二分查找题目leetcode总结
  9. linux命令文本处理(一)grep
  10. BIND rndc—使用说明
  11. jquery实现新浪微博的表情插件
  12. c语言系统主函数流程图,c语言流程图【调解方式】
  13. 蓝桥杯、PAT、CCF CSP、团体程序设计天梯赛、传智杯、全国高校计算机能力挑战赛、软考等大学生编程比赛/考试介绍
  14. java suite_Spring Tool Suite 配置和使用
  15. 列出各部门的最低工资MySQL_mysql练习题五
  16. xml测试_规范的XML测试
  17. 拯救报错:Error: connect ETIMEDOUT
  18. [软件工程 复习] 记录
  19. j3455linux网卡不亮,最新J3455主板直接安装黑群晖的若干问题解决办法
  20. RV32I控制转移指令的偏移量计算问题

热门文章

  1. QT自定义窗口插件在QT Creator的应用
  2. 看了两篇园子里的文章,初步懂了点接口的涵义
  3. 微软的system类库和java_Java常用类库----System 类
  4. Makefile —— Makefile的规则是什么?make是如何工作的?make的工作方式是什么?
  5. Mysql —— linux下使用c语言访问mySql数据库
  6. 洛谷 P2749 [USACO5.1]【夜空繁星Starry Night】
  7. redis配置主从复制
  8. sublime text 的小细节设置,让你的代码更优美
  9. android 自定义 listView
  10. 围观窗体与组件01 - 零基础入门学习Delphi23