前面写了一系列关于NIO和Netty基础相关的文章,对于学好Netty最主要的是什么呢?那应该是需要先学好NIO和理解Netty中的Reactor线程模型,还有关于网络编程方面的一些只是,譬如封帧、编解码等。
那么本篇文章就开始进行Netty源码的解析了,对于Netty源码的解析,我们还是先从代码运行链路的角度来进行源码解析,在进行源码解析之前,我们还需要先看下Netty Reactor线程模型图,如下:
上图中的含义大体如下:
在了解完Netty Reactor的相关概念之后,那么我们再来看一下关于Netty 使用的示例代码(本示例代码依赖的netty版本为:4.1.35.Final),如下:
public class NettyServer {
public static void main(String[] args) throws Exception {
//创建两个线程组:bossGroup和workGroup
//bossGroup 只处理连接请求,真正的和客户端的业务处理,是交由workGroup处理的
EventLoopGroup bossGroup = new NioEventLoopGroup(3);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
try {
// 创建服务器端的启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
// 使用链式编程来配置参数
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
// 使用NioServerSocketChannel作为服务器的通道实现
.channel(NioServerSocketChannel.class)
// 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
// 多个客户端同时来的时候,服务端将不能处理的客户端连接请求,将放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
//创建通道初始化对象,设置初始化参数,在 SocketChannel 建立起来之前执行
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//对workerGroup的SocketChannel设置处理器
socketChannel.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start。。");
ChannelFuture channelFuture = bootstrap.bind(9000).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 当客户端连接服务器完成就会触发该方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("客户端连接通道建立完成");
}
/**
* 读取客户端发送的数据
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @param msg 就是客户端发送的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//将 msg 转成一个 ByteBuf,类似NIO 的 ByteBuffer
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到客户端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
/**
* 数据读取完毕处理方法
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ByteBuf buf = Unpooled.copiedBuffer("HelloClient".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
/**
* 处理异常, 一般是需要关闭通道
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
}
public class NettyClient {
public static void main(String[] args) throws Exception {
//客户端需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建客户端启动对象
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group) //设置线程组
.channel(NioSocketChannel.class) // 使用NioSocketChannel作为客户端的通道实现
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//加入处理器
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
System.out.println("netty client start。。");
//启动客户端去连接服务器端
ChannelFuture cf = bootstrap.connect("127.0.0.1", 9000).sync();
//对通道关闭进行监听
cf.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 当客户端连接服务器完成就会触发该方法
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
ByteBuf buf = Unpooled.copiedBuffer("HelloServer".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
//当通道有读取事件时会触发,即服务端发送数据给客户端
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
上面展示了一下,关于Netty 使用的示例代码,本系列所有的代码解析都是从这些示例代码中出发的,当然前面的那张Netty Reactor线程模型图也是很重要的,需要读者详细了解,其中的相关关联关系,才能更好的深入了解Nettyd 源码。
因为本篇文章主要讲的是关于Netty 服务端启动源码的解析,所以这需要从 NettyServer 端的 bootstrap.bind(9000).sync() 这段代码开始入手,至于前面的EventLoopGroup、NioEventLoopGroup、ServerBootstrap、Bootstrap等相关组件的架构关系源码解析,后面将会用单独的篇章进行解析。
这里我们先看一下启动核心服务的时序图,从下图中可以清晰的看出关于NettyServer端示例代码中的主要流程,包括以下步骤:
从前面的示例中也是知道在Netty编程中,需要先创建一个Boss NioEventLoopGroup,在这个对象创建的时候会创建多个(这个多个是指自己传入或者根据CPU核心数进行相关计算的)NioEventLoop,在创建NioEventLoop时候,同时会绑定Selector,我们来看下代码的具体实现,如下:
// nThreads 参数是指传入的“多个”,可根据传入的数值来创建对应数量的NioEventLoop
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor)null);
}
public NioEventLoopGroup(int nThreads, Executor executor) {
// SelectorProvider.provider() 便是Nio编程中创建Selector的方式
this(nThreads, executor, SelectorProvider.provider());
}
public NioEventLoopGroup(int nThreads, Executor executor, SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
public NioEventLoopGroup(int nThreads, Executor executor, SelectorProvider selectorProvider, SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, new Object[]{selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()});
}
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
// 判断是否传入了 nThreads,如果没传,则使用默认的参数,否则根据传入的参数创建
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
this.terminatedChildren = new AtomicInteger();
this.terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
} else {
// 如果没有传入 executor,则创建 ThreadPerTaskExecutor
if (executor == null) {
executor = new ThreadPerTaskExecutor(this.newDefaultThreadFactory());
}
// 根据传入nThreads的数量,创建线程池数组,并赋值给 children
this.children = new EventExecutor[nThreads];
int j;
// 根据传入nThreads的数量,循环创建 NioEventLoop
for(int i = 0; i < nThreads; ++i) {
boolean success = false;
boolean var18 = false;
try {
var18 = true;
// 创建 NioEventLoop
this.children[i] = this.newChild((Executor)executor, args);
success = true;
var18 = false;
} catch (Exception var19) {
throw new IllegalStateException("failed to create a child event loop", var19);
} finally {
// 省略非关键代码
}
// 省略非关键代码
}
// 获取 chooser
this.chooser = chooserFactory.newChooser(this.children);
FutureListener
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(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
// 调用父类构造器
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
} else if (strategy == null) {
throw new NullPointerException("selectStrategy");
} else {
this.provider = selectorProvider;
// 获取selector
NioEventLoop.SelectorTuple selectorTuple = this.openSelector();
// 将获取到的selector 赋值给NioEventLoop中的Selector
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
this.selectStrategy = strategy;
}
}
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor, boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
// 创建TaskQueue
this.tailTasks = this.newTaskQueue(maxPendingTasks);
}
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor, boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {
super(parent);
this.threadLock = new Semaphore(0);
this.shutdownHooks = new LinkedHashSet();
this.state = 1;
this.terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = Math.max(16, maxPendingTasks);
this.executor = ThreadExecutorMap.apply(executor, this);
this.taskQueue = this.newTaskQueue(this.maxPendingTasks); // 创建TaskQueue
this.rejectedExecutionHandler = (RejectedExecutionHandler)ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
结合前面的Nettey Reactor模型图和前面的时序图,我们可以得知在创建 NioEventLoopGroup 的时候,会根据有没有传入线程数来判断创建多少个NioEventLoop,在没一个创建的NioEventLoop中,会绑定一个 Selector 和 TaskQueue。而创建Selector的方式,就是封装了NIO中获取方式。
在前面实力中在创建好两组NioEventLoopGroup后,便是继续创建ServerBootstrap,然后根据创建完ServerBootstrap的对象进行一些列链式调用ServerBootstrap中传入一些参数,譬如:两组NioEventLoopGroup、NioServerSocketChannel.class、Handler等。
调用io.netty.bootstrap.ServerBootstrap#group(io.netty.channel.EventLoopGroup, io.netty.channel.EventLoopGroup)方法传入的两组EventLoopGroup,在方法中只是进行了简单的赋值操作,这里就不多说了,而调用io.netty.bootstrap.AbstractBootstrap#channel方法,则是根据传入的Class类型来创建对应的类对象,那么来看下这个方法的代码是如何实现的,如下:
public B channel(Class extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
} else {
return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
}
}
public ReflectiveChannelFactory(Class extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException var3) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) + " does not have a public non-arg constructor", var3);
}
}
上面的代码很简单,我们可以看到根据传入channelClass,然后传入ReflectiveChannelFactory构造函数中,再在其中获取期传入channelClass的构造函数,这是为了后续使用做准备的。至于示例代码中所展示的调用io.netty.bootstrap.AbstractBootstrap#option方法,其中也只是作了简单的启动参数---··赋值操作,这里就不赘述了。至于最后调用的io.netty.bootstrap.ServerBootstrap#childHandler(io.netty.channel.ChannelHandler)方法,则是将自定义的一些Handler添加到Channel的Pipeline中,以便后续的业务调用中使用。
这里我们追踪下io.netty.bootstrap.AbstractBootstrap#bind(int)方法,当追踪到io.netty.bootstrap.AbstractBootstrap#doBind方法即可,核心代码如下:
private ChannelFuture doBind(final SocketAddress localAddress) {
// 初始化并注册Channel
final ChannelFuture regFuture = this.initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
} else if (regFuture.isDone()) { //不能肯定register完成,因为register是丢到nio event loop里面执行去了。
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel);
// 等着register完成,再来通知执行bind
regFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 创建Channel,
channel = this.channelFactory.newChannel();
// 初始化Channel
this.init(channel);
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
// 开始 register channel
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
在上面的AbstractBootstrap#doBind方法中,主要的调用逻辑是initAndRegister方法的调用和doBind0方法的调用。在initAndRegister方法中最要是创建channel和注册channel。
创建channel这里就会有前面提到的调用io.netty.bootstrap.AbstractBootstrap#channel方法是为后面做准备的,就是为这里创建Channel做准备的,io.netty.channel.ReflectiveChannelFactory#newChannel方法实现如下:
public T newChannel() {
try {
// 通过构造函数创建实例
return (Channel)this.constructor.newInstance();
} catch (Throwable var2) {
throw new ChannelException("Unable to create Channel from class " + this.constructor.getDeclaringClass(), var2);
}
}
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
// 调用 NioServerSocketChannel 的父类构造器
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
// 调用 AbstractNioChannel 父类构造器
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
// 非阻塞模式(对NIO的封装)
ch.configureBlocking(false);
} catch (IOException e) {
// 省略非关键代码
}
}
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
// 创建 pipeline
pipeline = newChannelPipeline();
}
上面是整个Channel创建的流程,其中包含了对NIO的封装,以及最后创建了一个Pipeline等。在调用 NioServerSocketChannel 的父类构造器的时候,传入了一个OP_ACCEPT事件,这个事件的注册,后面会对其进行解析。创建Pipeline是为了给前面提到的ServerBootstrap链式调用中说到的Handler创建并放入pipeline做准备的。
这里就是前面时序图中所说的,初始化 NioServerSocketChannel了,初始化Channel的调用是在io.netty.bootstrap.AbstractBootstrap#initAndRegister里面。那么来看下这个方法的实现,代码如下:
void init(Channel channel) throws Exception {
Map, Object> options = this.options0();
synchronized(options) {
setChannelOptions(channel, options, logger);
}
Map, Object> attrs = this.attrs0();
synchronized(attrs) {
Iterator var5 = attrs.entrySet().iterator();
while(true) {
if (!var5.hasNext()) {
break;
}
Entry, Object> e = (Entry)var5.next();
AttributeKey key = (AttributeKey)e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = this.childGroup;
final ChannelHandler currentChildHandler = this.childHandler;
final Entry[] currentChildOptions;
synchronized(this.childOptions) {
currentChildOptions = (Entry[])this.childOptions.entrySet().toArray(newOptionArray(0));
}
final Entry[] currentChildAttrs;
synchronized(this.childAttrs) {
currentChildAttrs = (Entry[])this.childAttrs.entrySet().toArray(newAttrArray(0));
}
// ChannelInitializer一次性、初始化handler:
// 负责添加一个ServerBootstrapAcceptor handler,添加完后,自己就移除了:
// ServerBootstrapAcceptor handler: 负责接收客户端连接创建连接后,对连接的初始化工作。
p.addLast(new ChannelHandler[]{new ChannelInitializer() {
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = ServerBootstrap.this.config.handler();
if (handler != null) {
pipeline.addLast(new ChannelHandler[]{handler});
}
ch.eventLoop().execute(new Runnable() {
public void run() {
pipeline.addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
}
});
}
}});
}
注册Channel是在io.netty.bootstrap.AbstractBootstrap#initAndRegister方法中config().group().register(channel)代码的调用,config().group()就是获取所创建的NioEventLoopGroup,它里面有多个NioEventLoop,那我们来看下这个register(channel)方法的具体实现,代码如下:
io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
@Override
public ChannelFuture register(Channel channel) {
// 注册
return next().register(channel);
}
@Override
public EventLoop next() {
// 从EventLoopGroup中取一个EventLoop
return (EventLoop) super.next();
}
上面的register方法中通过调用next()方法,来调用每一个NioEventLoop来注册Channel,这个register方法的实现具体如下:
io.netty.channel.AbstractChannel.AbstractUnsafe#register
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
// 这个方法中的eventLoop.execute(...)也是NioEventLoop启动的时机
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
// 省略非关键代码
AbstractChannel.this.eventLoop = eventLoop;
// 判断是否是当前线程的
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
// 省略非关键代码
}
}
}
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
}
boolean firstRegistration = this.neverRegistered;
// 具体的注册操作
AbstractChannel.this.doRegister();
this.neverRegistered = false;
AbstractChannel.this.registered = true;
AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
this.safeSetSuccess(promise);
AbstractChannel.this.pipeline.fireChannelRegistered();
//server socket的注册不会走进下面if,server socket接受连接创建的socket可以走进去。因为accept后就active了。
if (AbstractChannel.this.isActive()) {
if (firstRegistration) {
// 监听OP_ACCEPT事件
AbstractChannel.this.pipeline.fireChannelActive();
} else if (AbstractChannel.this.config().isAutoRead()) {
this.beginRead();
}
}
} catch (Throwable var3) {
// 省略非关键代码
}
}
上面代码中的io.netty.channel.AbstractChannel.AbstractUnsafe#register方法首次执行时,NioEventLoop中的线程并没有启动,因此eventLoop.inEventLoop()返回的是false,任务便直接通过eventLoop.execute(...)方法来执行,而eventLoop.execute(...)方法的执行会导致线程启动。这就是把任务提交到NioEventLoop,来启动线程的代码,代码具体实现如下:
@Override
public void execute(Runnable task) {
// 省略非关键代码
boolean inEventLoop = inEventLoop();
addTask(task);
if (!inEventLoop) {
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
}
if (reject) {
reject();
}
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
io.netty.channel.nio.AbstractNioChannel#doRegister
当任务任务提交到NioEventLoop,来启动线程的时候便调用io.netty.channel.AbstractChannel.AbstractUnsafe#register0方法,代码实现具体如下:
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 把 ServerSocketChannel注册到Selector上,但是这里注册的是“0”,而非事件(对NIO的封装)
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
监听OP_ACCEPT事件
关于监听事件的调用是在io.netty.channel.AbstractChannel.AbstractUnsafe#register0方法中的pipeline.fireChannelActive()这行代码进行调用的,那么io.netty.channel.DefaultChannelPipeline#fireChannelActive方法代码逻辑大体如下:
@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}
private void invokeChannelActive() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
上面代码中最后readIfIsAutoRead()方法中的channel.read()方法最终将会执行OP_ACCEPT事件注册过程。
本篇文章主要是根据Netty Reactor和时序图进行的相关讲解: