Github地址:https://github.com/lhj502819/netty/tree/v502819-main,示例代码在example模块中
系列文章
- 你知道都有哪些I/O模型吗?
- Java NIO三大角色Channel、Buffer、Selector
- Doug lea《Scalable IO in Java》翻译
- Reactor模型你知道都有哪些吗?
- Netty服务端创建源码流程解析
- EventLoopGroup到底是个啥?
- 未完待续…
创作不易,如果对您有帮助,麻烦辛苦下小手点个关注,有任何问题都可以私信交流哈。
祝您虎年虎虎生威。
在Netty中,Channel为网络操作抽象类,EventLoop负责处理注册到其上的Channel处理I/O操作,两者配合参与I/O操作;
EventLoopGroup是一个EventLoop的分组,它可以获取到一个或者多个EventLoop对象,因此它提供了迭代出EventLoop对象的方法。
在我们的使用示例中,使用到的就是:
- new NioEventLoopGroup(),创建一个EventLoopGroup对象
- EventLoopGroup#register(Channel channel),将Channel注册到EventLoopGroup上
红色部分为EventLoopGroup相关类,今天我们只学习这部分的内容
EventExecutorGroup
EventExecutorGroup负责提供EventExecutor以通过其next()方法使用。 除此之外,它还负责处理它们的生命周期并允许以全局方式关闭它们。
继承了ScheduledExecutorService
和Iterable<EventExecutor>
,表明了是可定时的,并且提供EventExecutor存储及遍历功能。
EventExecutorGroup自身不执行任务,而是将任务submit
或者schedule
给自己管理的EventExecutor的分组,至于提交给哪一个EventExecutor,一般是通过next()
方法选择一个EventExecutor,但是也只是提供了行为,具体的还要看实现类。
AbstractEventExecutorGroup
对接口进行了抽象实现,但并未具体实现,比如{@link #submit(Runnable)} 只是提供了提交任务的方式,但具体实现还是由子类去实现。代码就不贴了,感兴趣大家自己看下就行哈,很简单的。
MultithreadEventExecutorGroup
通过名称就可以看出来,是EventExecutor的多线程版本。
变量
/**
* EventExecutor集合
*/
private final EventExecutor[] children;
/**
* 只读的EventExecutor数组
*/
private final Set<EventExecutor> readonlyChildren;
/**
* 已终止的Executor数量
*/
private final AtomicInteger terminatedChildren = new AtomicInteger();
/**
* 用于终止EventExecutor的异步Future
*/
private final Promise<?> terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);
/**
* EventExecutor选择器
*/
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
构造方法
/**
* Create a new instance.
*
* @param nThreads the number of threads that will be used by this instance.
* @param threadFactory the ThreadFactory to use, or {@code null} if the default should be used.
* @param args arguments which will passed to each {@link #newChild(Executor, Object...)} call
*/
protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
this(nThreads, threadFactory == null ? null : new ThreadPerTaskExecutor(threadFactory), args);
}
/**
* 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 args arguments which will passed to each {@link #newChild(Executor, Object...)} call
*/
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
/**
* 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) {
checkPositive(nThreads, "nThreads");
/**
* 如果传入的执行器为空,则使用默认的执行器ThreadPerTaskExecutor
*/
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//创建EventExecutor数组
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//创建Executor对象
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) {
//如果创建失败,则关闭所有已创建的EventExecutor
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
//确保所有EventExecutor都已经关闭
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;
}
}
}
}
}
//创建EventExecutor选择器
chooser = chooserFactory.newChooser(children);
//创建监听器,用于监听EventExecutor终止,
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
//回调逻辑,当所有EventExecutor都终止完成时,通过调用Promise#setSuccess方法,通知监听器们
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
//设置监听器到每个EventExecutor上
e.terminationFuture().addListener(terminationListener);
}
//创建不可变的EventExecutor数组
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
注意事项(这里我们重点关注最终的构造方法):
- EventExecutorChooserFactory默认使用的是DefaultEventExecutorChooserFactory,用来在多个Executor中选择一个Executor去执行任务
- 如果我们没有指定executor,那么会使用ThreadPerTaskExecutor(下边有提到)
#49行
创建Executor,会为每个线程都创建一个Executor,随后会通过next()方法访问到,具体的创建过程由子类实现
ThreadPerTaskExecutor
每个任务一个线程的的执行器实现类
EventLoopGroup
继承了EventExecutorGroup
,提供注册Channel能力的EventExecutorGroup。
MultithreadEventLoopGroup
继承了MultithreadEventExecutorGroup
,又实现了EventLoopGroup
,基于MultithreadEventExecutorGroup
对Channel
的注册进行行为抽象。
static {
/**
* 当未指定线程数时,默认的线程数为 CPU核心数 * 2 ,因为目前的CPU都是超线程,一个CPU可对应2个线程
*/
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
@Override
public ChannelFuture register(Channel channel) {
//获取一个EventLoop,将Channel注册上去
return next().register(channel);
}
NioEventLoopGroup
基于Java NIO Selector的Executor实现,NioEventLoop实例就是在这里创建的,Selector也是在这里和EventLoop绑定的,同时也对JDK使用epoll的bug作了处理。
/**
* 我们重点关注下其构造方法的各个参数即可,这里也看到了我们心心念念的Selector
* @param nThreads the number of threads that will be used by this instance.
* @param executor the Executor to use, or {@code null} if default one should be used.
* @param chooserFactory the {@link EventExecutorChooserFactory} to use.
* @param selectorProvider the {@link SelectorProvider} to use.
* @param selectStrategyFactory the {@link SelectStrategyFactory} to use.
* @param rejectedExecutionHandler the {@link RejectedExecutionHandler} to use.
* @param taskQueueFactory the {@link EventLoopTaskQueueFactory} to use for
* {@link SingleThreadEventLoop#execute(Runnable)},
* or {@code null} if default one should be used.
* @param tailTaskQueueFactory the {@link EventLoopTaskQueueFactory} to use for
* {@link SingleThreadEventLoop#executeAfterEventLoopIteration(Runnable)},
* or {@code null} if default one should be used.
*/
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
SelectorProvider selectorProvider,
SelectStrategyFactory selectStrategyFactory,
RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory taskQueueFactory,
EventLoopTaskQueueFactory tailTaskQueueFactory) {
super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory,
rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}
/**
* 创建NioEventLoop,用来执行事件,这里的args就是从构造方法中传到父类的,父类中又调用子类实现,前后呼应有没有,
* 是前后呼应,但我也没感觉到这种设计的优点^_^
*/
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
SelectorProvider selectorProvider = (SelectorProvider) args[0];
SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];
EventLoopTaskQueueFactory taskQueueFactory = null;
EventLoopTaskQueueFactory tailTaskQueueFactory = null;
int argsLength = args.length;
if (argsLength > 3) {
taskQueueFactory = (EventLoopTaskQueueFactory) args[3];
}
if (argsLength > 4) {
tailTaskQueueFactory = (EventLoopTaskQueueFactory) args[4];
}
return new NioEventLoop(this, executor, selectorProvider,
selectStrategyFactory.newSelectStrategy(),
rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}
/**
* Replaces the current {@link Selector}s of the child event loops with newly created {@link Selector}s to work
* around the infamous epoll 100% CPU bug.
*
* 用来处理JDK使用epoll的100%CPU bug,当触发bug时,NioEventLoop会自动调用此方法进行重建Selector对象,以修复该问题
*/
public void rebuildSelectors() {
for (EventExecutor e: this) {
((NioEventLoop) e).rebuildSelector();
}
}
/**
* Replaces the current {@link Selector} of this event loop with newly created {@link Selector}s to work
* around the infamous epoll 100% CPU bug.
*
* 看这注释表达了作者对JDK NIO的不满,哈哈,臭名昭著的bug
*/
public void rebuildSelector() {
if (!inEventLoop()) {
execute(new Runnable() {
@Override
public void run() {
rebuildSelector0();
}
});
return;
}
rebuildSelector0();
}
总结
EventLoopGroup主要就是对Executor进行的封装,并且是基于JUC的包进行的实现,作者对各个工作组件都进行了很细化的的拆分,也是为了扩展性高。文章中我没有把所有的源码都COCY出来逐句翻译,我们在阅读源码的过程中不用去把每一行代码都搞懂,我们要去体会框架设计的巧妙,对整个框架的内部逻辑走向把握住即可。下一篇文章我们会对EventLoop进行分析,敬请期待.
我是壹氿,感谢各位小伙伴点赞、收藏和评论,文章持续更新,我们下期再见!
也可以加我的个人VX交流沟通:lhj502819,一起努力冲击大厂,另外有很多学习以及面试的材料提供给大家。