Netty(四)_核心源码分析
文章目录
本篇内容门槛还是有的,需要你对我前三篇netty所说的内容不算陌生
为了提高阅读舒适性,我们并不会一行一行代码解析,并删除部分与目的无关的源码。读源码要带着目的性读,最好你已经知道它背后基本原理,然后带着审视的感觉去进行阅读,反客为主。
在整个流程中,以下这张图要常驻脑海中!
我们选择netty提供的example程序,该程序可以在源码包netty.example.echo
包
public final class EchoServer {
//...
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure SSL.
//SSL配置 用于https
// Configure the server.
// 服务端配置
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 服务端自定义的处理器Handler 这里不进行代码展示
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
//引导器
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) //设置服务端通道
.option(ChannelOption.SO_BACKLOG, 100) //通道配置选项,连接队列容量100
.handler(new LoggingHandler(LogLevel.INFO))//设置服务端通道处理器Handler
.childHandler(new ChannelInitializer<SocketChannel>() {
//客户端通道的处理器
@Override
//客户端通道完成初始化时
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
//向管道链中添加处理器
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//向管道链添加自定义处理器
p.addLast(serverHandler);
}
});
// Start the server.
//异步绑定端口
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
//阻塞直至服务端通道关闭 --> 维持服务端一直提供服务
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
//优雅地关闭所有资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
客户端:
public final class EchoClient {
//属性
static final boolean SSL = System.getProperty("ssl") != null;
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
public static void main(String[] args) throws Exception {
// Configure SSL.git
//...
// Configure the client.
//客户端只需要一个线程组 以下引导与服务端类似 不赘述
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect(HOST, PORT).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
}
}
EventLoopGroup实例化源码分析
我们这里从最具有一般性的服务端的EventLoopGroup workerGroup = new NioEventLoopGroup();
入手,看看线程池创建中的细节。
这一步是在服务端绑定端口号之间完成的,主要是为了把所有线程池进行实例化
在追溯源码前,我们根据对EventLoopGroup的认知,即
EventLoopGroup 是一组 EventLoop 的抽象,EventLoopGroup代表着线程池, EventLoop代表线程池中的线程。
在netty中BossGroup和WorkGroup都是NioEventLoopGroup类型。
每个 EventLoop 维护着一个 Selector 实例,Selector的作用是在Group中选择Loop来执行任务。
这里先看看NioEventLoopGroup中参数最全的构造器,看看NioEventLoopGroup的实例化会配置什么参数,或者说,一个Group有什么特性/属性。
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory,
final RejectedExecutionHandler rejectedExecutionHandler,
final EventLoopTaskQueueFactory taskQueueFactory) {
super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory,
rejectedExecutionHandler, taskQueueFactory);
}
参数说明:
- nThreads:线程池中的线程数,也就是 NioEventLoop 的实例数量。
- executor:Java线程池(Executor)
- chooserFactory:线程池选择线程(next)来执行任务的选择策略。追源码的过程中我们也可以看看具体有什么策略。
- selectorProvider:通过它能实例化 Selector,每个线程池都持有一个 selectorProvider 用以实例化selector。
- selectStrategyFactory:这个涉及到的是线程池中线程的工作流程。
- rejectedExecutionHandler:用于处理线程池中没有可用的线程来执行任务的情况的处理器。在 Netty 中稍微有一点点不一样,这个是给 NioEventLoop 实例用的,后面说。
- EventLoopTaskQueueFactory,顾名思义,EventLoop的工作队列。
从无参构造器开始跟踪,看看这些参数都是怎么被赋值的,并且被赋了什么值。
public class NioEventLoopGroup extends MultithreadEventLoopGroup {
//默认构造器
public NioEventLoopGroup() {
this(0);
}
//上面构造器所调用
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
//上面构造器所调用
public NioEventLoopGroup(int nThreads, Executor executor) {
//SelectorProvider.provider()返回selectorProvider
this(nThreads, executor, SelectorProvider.provider());
}
//上面构造器所调用
public NioEventLoopGroup(
int nThreads, Executor executor, final SelectorProvider selectorProvider) {
//涉及到线程在做 select 操作和执行任务过程中的策略选择问题
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
//上面构造器所调用
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
//rejectedExecutionHandler(线程池无线程可用的拒绝策略)默认值为RejectedExecutionHandlers.reject()
//点进.reject()发现默认拒绝策略最终抛出异常
//注意,这里将交由父类构造器
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
到父类 MultithreadEventLoopGroup 的构造方法中
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
这里将nThreads重新设置为DEFAULT_EVENT_LOOP_THREADS
该类中的静态代码块能获取本机CPU核数,并将之*2作为nThreads
static {
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);
}
}
继续往下走,来到父类MultithreadEventExecutorGroup这个构造器
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
//这里重新设置了线程池选择策略
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
这里我们来看看它的选择策略是什么样的
点进DefaultEventExecutorChooserFactory类
//这个方法根据executors的不同,返回不同的策略
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
//如果2^n个线程数
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
//如果不是
return new GenericEventExecutorChooser(executors);
}
}
//我们再看看两个策略的next()方法,之前说过next()是线程池用来选择线程的方法
@Override
//如果2^n个线程数 与
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
@Override
//如果2^n个线程数 模
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
继续构造器:下面这个明显不一样了。我们关注其核心语句
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
//这里实例化executor,即把Java线程池对象进行实例化
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//EventExecutor类型数组,即线程池,其元素是每个线程,即每个Loop
//可用看出EventLoop最终类型是EventExecutor而不说线程对象Thread
children = new EventExecutor[nThreads];
//实例化 children 数组中的每一个元素,即实例化Group中每个loop
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//newChild(executor, args)为每个Loop的实例化入口,重点关注。
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
//...
} finally {
//如果Loop实例化失败
if (!success) {
//之前已实例化的Loop全部关闭
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
// 等待这些线程成功 shutdown
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;
}
}
}
}
}
//通过之前设置的 chooserFactory 来实例化 Chooser即Group的选择策略,传入children用于根据线程数返回不同的choser
chooser = chooserFactory.newChooser(children);
// 设置一个 Listener 用来监听该线程池的 termination 事件即结束事件
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(