56. Netty源代码分析-服务器初始化 NioEventLoopGroup实例化
一. 代码下载
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());}
我们可以看到几点
- NioEventLoopGroup采用的是构造函数重载的方式,以适应不同的初始化场景
- Executor传的是null
- SelectorProvider用的是SelectorProvider.provider()
- 然后把构造好的参数都传给父类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);}
}
这段代码我们也可以看到几点:
- SelectorProvider provider是一个单例,static类型
- SelectorProvider.provider的实现,产生了一个KQueueSelectorProvider
- 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));}
上面这段代码我们可以看到这几点:
- 如果我们实例化NioEventLoopGroup没有传入参数,也就是没有nThreads,那么就会采用默认的DEFAULT_EVENT_LOOP_THREADS
DEFAULT_EVENT_LOOP_THREADS如果没有配置io.netty.eventLoopThreads的话,一般是cpu核数*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;
- children - EventExecutor数组,大小是nThreads,线程数目。
- 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);}...
}
从上面这段代码我们可以看出这几点
- NioEventLoop里面保存了SelectorProvider selectorProvider, Selector selector, unwrappedSelector(类型是KQueueSelectorImpl)
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);
上面代码我们可以看到:
- chooserFactory的类型是DefaultEventExecutorChooserFactory,所以newChooser调用的是DefaultEventExecutorChooserFactory.newChooser方法。
如下:
public EventExecutorChooser newChooser(EventExecutor[] executors) {if (isPowerOfTwo(executors.length)) {return new PowerOfTwoEventExecutorChooser(executors);} else {return new GenericEventExecutorChooser(executors);}}
- 传入的参数是children,也就是NioEventLoop数组
- DefaultEventExecutorChooserFactory INSTANCE是一个static final类型对象,也就是一种饿汉式的单例模式,如下:
public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();
继续看newChooser的实现
2.2.8 newChooser
newChooser的代码就不贴了,上面就有,从上面代码可以看到:
- 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。再做&运算。
大家自己可以拿到数字举个例子,比较巧妙。后续自己写代码可以借鉴,这是读源码的一个好处,可以学习到别人很多优秀的写法。
- 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实例化相关推荐
- java derby 网络模式,Derby源代码分析 -- 服务器启动
一直以来都想研究下数据库的实现原理,Derby在我眼里绝对是一个完美的切入点.首先它是100%纯Java实现,对于我这种Java程序员来说简直完美的,不需要去懂C,C++...等等无疑减少了很大的难度 ...
- Nouveau源代码分析(三):NVIDIA设备初始化之nouveau_drm_probe
Nouveau源代码分析(三) 向DRM注冊了Nouveau驱动之后,内核中的PCI模块就会扫描全部没有相应驱动的设备,然后和nouveau_drm_pci_table对比. 对于匹配的设备,PCI模 ...
- SDL2源代码分析1:初始化(SDL_Init())
===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...
- 【Netty】使用 Netty 开发 HTTP 服务器
文章目录 一. HTTP 服务器开发 二. HTTP 服务器代码分析 1 . Netty 开发 HTTP 服务器与 TCP 服务器对比 2 . ChannelInitializer 设置 3 . 自定 ...
- 区块链教程Fabric1.0源代码分析scc(系统链码)
区块链教程Fabric1.0源代码分析scc(系统链码),2018年下半年,区块链行业正逐渐褪去发展之初的浮躁.回归理性,表面上看相关人才需求与身价似乎正在回落.但事实上,正是初期泡沫的渐退,让人们更 ...
- CASSINI源代码分析
CASSINI源代码分析 2004-11-10 http://blog.csdn.net/shanhe/ 为什么要分析CASSINI? Cassini(卡西尼)是asp.net上的一个开源项目.主要给 ...
- 【Netty】使用 Netty 开发 HTTP 服务器 ( HTTP 请求过滤 )
文章目录 一. HTTP 服务器请求过滤功能 1 . HTTP 服务器资源请求过滤 2 . HTTP 请求过滤方法 二. HTTP 服务器 ( 资源过滤 ) 代码实现 1 . 服务器主程序 2 . 服 ...
- 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t
新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...
- Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析
[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重劳动成果] 1 背景 之所以写这一篇博客的原因是由于之前有写过一篇<Android应用setCont ...
最新文章
- 有向图——强连通分量
- Python 实现程序的单一实例
- MySql数据库使用入门
- mysql command line client和mysql.exe输入密码后闪退问题解决方法
- ubuntu中解压rar文件遇到乱码的解决方法
- 管理者必看!深度剖析BI与数据仓库,企业能否成功转型就看它
- 弃用数据库自增ID,曝光一下我自己用到的解决方法之---终结篇
- leetcode—22.二分查找题目leetcode总结
- linux命令文本处理(一)grep
- BIND rndc—使用说明
- jquery实现新浪微博的表情插件
- c语言系统主函数流程图,c语言流程图【调解方式】
- 蓝桥杯、PAT、CCF CSP、团体程序设计天梯赛、传智杯、全国高校计算机能力挑战赛、软考等大学生编程比赛/考试介绍
- java suite_Spring Tool Suite 配置和使用
- 列出各部门的最低工资MySQL_mysql练习题五
- xml测试_规范的XML测试
- 拯救报错:Error: connect ETIMEDOUT
- [软件工程 复习] 记录
- j3455linux网卡不亮,最新J3455主板直接安装黑群晖的若干问题解决办法
- RV32I控制转移指令的偏移量计算问题
热门文章
- QT自定义窗口插件在QT Creator的应用
- 看了两篇园子里的文章,初步懂了点接口的涵义
- 微软的system类库和java_Java常用类库----System 类
- Makefile —— Makefile的规则是什么?make是如何工作的?make的工作方式是什么?
- Mysql —— linux下使用c语言访问mySql数据库
- 洛谷 P2749 [USACO5.1]【夜空繁星Starry Night】
- redis配置主从复制
- sublime text 的小细节设置,让你的代码更优美
- android 自定义 listView
- 围观窗体与组件01 - 零基础入门学习Delphi23