Netty——源码解析
零、前言
在 io.netty.example 包下,有很多 Netty 源码案例,可以用来分析。
一、Netty服务器启动源码
-
源码需要剖析到 Netty 中调用 doBind 方法为止,即追踪到 NioServerSocketChannel 的 doBind。
-
同时可以追踪到 NioEventLoop 类的 run 方法,它是无限循环,在服务器端运行。
Netty服务器启动逻辑:
public final class EchoServer {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 服务端启动引导工具类
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.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(new EchoServerHandler());
}
});
// 通过 bind 启动服务
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
- NioEventLoopGroup是整个 Netty 的核心对象,bossGroup 用于接受 TCP 请求,它会将请求交给 workerGroup,workerGroup 会获取到真正的连接,然后和连接进行通信,比如读写解码编码等操作。
- 创建了一个 ServerBootstrap 对象,它是一个引导类,用于启动服务器和引导整个程序的初始化,它和 ServerChannel 关联。
- 调用 group 方法将两个 group 放入了自己的字段中。
- group() 方法
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
// 调用父类方法
super.group(parentGroup);
...
// 直接赋值给字段
this.childGroup = childGroup;
return this;
}
// super.group()
public B group(EventLoopGroup group) {
...
this.group = group;
return self();
}
- 然后添加了一个 Channel,其中参数为一个 Class 对象,引导类将通过这个 Class 对象反射创建 ChannelFactory,然后添加了一些 TCP 的参数。
- 添加一个 SocketChannel(不是 ServerSocketChannel)的 handler。
- 然后绑定端口并阻塞至连接成功,通过bind启动服务。
- 最后 main 线程阻塞等待网络服务被关闭。
1.1 NioEventLoopGroup 的创建及 NioEventLoop 的启动执行
- EventLoopGroup 实现了EventExcutor接口和 ScheduledExecutorService,因此是一个线程池。
- 本质是创建了 EventExecutor 数组,它也实现了ScheduledExecutorService。
- Executor线程池本质上就是创建多个NioEventLoop时间执行器。
- NioEventLoop的实现就是NIO中的Selector增强,本质还是Selector,而NioEventLoop是不能创建线程的,因此用到Executor创建线程。
- 线程run方法中会轮询执行selector.select方法和taskqueue里的内容,每个EventLoop对应一个轮询线程。
- EventLoopGroup 是事件循环组(线程组),含有多个 EventLoop,可以注册 Channel,用于在事件循环中去进行选择。
- new NioEventLoopGroup(1): 1 表示 bossGroup 事件组有 1 个线程可以指定,如果 new NioEventLoopGroup() 会含有默认的cpu核数 * 2个线程。
- MultithreadEventExecutorGroup 负责创建事件循环组
抽象类 MultithreadEventExecutorGroup 的构造方法是 NioEventLoopGroup 真正的构造处,这里可以看成是一个模板方法。
// nThreads:使用的线程数
// executor:执行器
// chooserFactory:单例
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
...
// 传入 null, 采用 Netty 默认的线程工厂和默认的执行器 ThreadPerTaskExecutor
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 创建 EventExecutor 数组
children = new EventExecutor[nThreads];
// 初始化线程数组
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 创建 NioEventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
...
} finally {
// 如果创建失败,优雅关闭
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
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) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
// 根据线程选择工厂创建一个线程选择器
chooser = chooserFactory.newChooser(children);
...
// 为每一个单例线程池添加一个关闭监听器
for (EventExecutor e: children) {
e.termin