这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记
随着项目规模和复杂度的增加,需要把项目拆成很多小的服务,服务之间的通讯就要用到RPC。
遇到的一个问题:
-
在写的过程中,我也有了解netty底层的逻辑,就是Reactor模型。之前也有了解过这个模型,但是写的时候细看,发现bossgroup用来处理select的链接(客户端的接收和网络读写事件的处理),workergroup来解析和处理任务,这个我是理解的,但是一般netty使用的时候,都没有再开一个线程池来处理任务,也就是说如果这个时候有任务要花很长时间,那岂不是拖慢了后面任务的处理?这时候看了一下netty源码
-
因为之前用的是Java nio,就是在select之后,判断是建立链接还是可读,如果可读的话,把任务交给线程池来处理。但是对于netty,workergroup在select之后,并没有交给线程池,而是自己就直接处理了,这里我不太理解,因为如果自己处理的话,不就相当于每个任务之间是串行,效率是很低的,并没有利用到线程池来提升效率。我先明确一下自己理解的逻辑,也就是“n大小的eventloopgroup是不是就有n个线程,同时一个eventloop就对应一个线程”。于是我又去找了一下相应的资料,看了一下源码,首先捋顺了一下线程的逻辑,这个eventloopGroup其实就是一个multiThreadEventLoopGroup,然后EventLoop是一个SingleThreadEventLoop,然后底层是个Executor,是在把任务提交和任务执行进行解耦。
- 类图,Executor,最佳实践
回到刚刚问题的答案,也就是为什么每个任务是串行的,在ChannelPipeline接口的注释里可以找到解释:
Netty建议使用它自身提供的业务线程池来驱动非I/O的耗时业务逻辑,如果业务逻辑执行时间很短或者是完全异步的,那么不需要使用额外的非I/O线程池。而且业务线程池的具体用法是Netty在添加handler时,在ChannelPipeline接口提供了一个重载的addLast方法,专用于为对应handler添加Netty业务线程池
// NioEventLoopGroup
// 也就是创建新的eventloop,eventloop是继承singleThreadEventLoop的,所以就相当于一个
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}
//
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
// 创建线程个数的child,创建child是在上层定义了的
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
children[i] = newChild(executor, args);
}