1、Netty之Server端注册Accept事件Read事件的关注

编程入门 行业动态 更新时间:2024-10-23 21:27:29

1、Netty之Server端注册Accept<a href=https://www.elefans.com/category/jswz/34/1770959.html style=事件Read事件的关注"/>

1、Netty之Server端注册Accept事件Read事件的关注

任务系列,在Server端启动过程中存在诸多任务:

  1. 端口绑定任务。
  2. 隐式添加handler之ServerBootstrapAcceptor任务。
  3. NioServerSocketChannel在Selector注册任务。
  4. 事件循环组任务。

客户端启动后与服务端建立tcp连接后,BossLoopGroup reactor线程维护的事件循环组将检测到连接事件,对应于触发服务端的Accept事件,此时涉及的任务包含:

  1. 服务端通过事件循环组得到客户端NioSocketChannel,涉及NioSocketChannel在其对应的Selector注册任务。

1、 启动剖析

Netty 中ServerSocketChannel对应的是NioServerSocketChannel。通过附件attachment将NioServerSocketChannel与原生jdk的ssc联系起来。
通过serverBootstrap.channel(NioServerSocketChannel.class)配置Netty服务端的ServerSocketChannel用于【绑定端口地址】以及【创建客户端SocketChannel】。Netty中的NioServerSocketChannel.class就是对JDK NIO中ServerSocketChannel的封装。而用于表示客户端连接的NioSocketChannel是对JDK NIO SocketChannel封装。

ServerBootstrap持有的是WorkLoopGroup,AbstractBootstrap持有的是BossLoopGroup。

1.1、AbstractBootstrap#doBind【存在端口绑定任务】

private ChannelFuture doBind(final SocketAddress localAddress) {// 1. 异步创建,初始化,注册ServerSocketChannel到main reactor上final ChannelFuture regFuture = initAndRegister();final Channel channel = regFuture.channel();...// 2. 因为是 initAndRegister 异步执行,需要分两种情况来看,调试时也需要通过 suspend 断点类型加以区分if (regFuture.isDone()) {ChannelPromise promise = channel.newPromise();// 3.1 立刻调用 doBind0doBind0(regFuture, channel, localAddress, promise);return promise;} // 2.2 还没有完成else {//如果此时注册操作没有完成,则向regFuture添加operationComplete回调函数,注册成功后回调。final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);// 3.2 回调 doBind0regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {Throwable cause = future.cause();if (cause != null) {// 处理异常...promise.setFailure(cause);} else {promise.registered();// 3. 由注册线程去执行 serverSocketChannel向Main Reactor注册成功后开始绑定端口doBind0(regFuture, channel, localAddress, promise);}}});return promise;}
}

initAndRegister:主要完成两件任务初始化 & 注册。所谓的初始化是服务端套接字NioServerSocketChannel添加ChannelInitializer之ServerBootstrapAcceptor。所谓的注册是将当前套接字NioServerSocketChannel完成在Selector上的注册。

这里有一个问题,执行doBind0方法的线程正是Reactor线程,为什么不直接在这里去执行bind操作,而是再次封装成异步任务提交给Reactor中的taskQueue呢?反正最终都是由Reactor线程执行,这其中又有什么分别呢?

doBind0方法的调用是由ioty.channel.AbstractChannel.AbstractUnsafe#register0方法在将NioServerSocketChannel注册到Main Reactor之后,并且NioServerSocketChannel的pipeline已经初始化完毕后,通过safeSetSuccess方法回调过来的。

这个过程全程是由Reactor线程来负责执行的,但是此时register0方法并没有执行完毕,还需要执行后面的逻辑。

而绑定逻辑需要在注册逻辑执行完之后执行,所以在doBind0方法中Reactor线程会将绑定操作封装成异步任务先提交给taskQueue中保存,这样可以使Reactor线程立马从safeSetSuccess中返回,继续执行剩下的register0方法逻辑。

当Reactor线程执行完register0方法后,就会从taskQueue中取出异步任务执行。

1.2、AbstractBootstrap#initAndRegister

final ChannelFuture initAndRegister() {Channel channel = null;// 创建Nio原生的 ServerSocketChannel ssc = ServerSocketChannel.open();channel = channelFactory.newChannel();init(channel);//ServerBootstrap#init// 注册 - 通过 bossGroup 将原生 channel 注册到 selector 上ChannelFuture regFuture = config().group().register(channel);...return regFuture;
}

config().group().register(channel):

  1. 通过AbstractBootstrap得到BossLoopGroup。
  2. 通过NioEventLoopGroup利用【EventExecutorChooserFactory.EventExecutorChooser】找到MainLoopGroup中Reactor【EventLoopGroup】实现NioServerSochetChannel在Selector上的注册功能。

步骤1参考:NioServerSochetChannel初始化过程

1.2.1、ServerBootstrap#init【存在handler之ServerBootstrapAcceptor隐式添加任务】

void init(Channel channel) throws Exception {...ChannelPipeline p = channel.pipeline();// 由套接字抽象类AbstractChannel实例化ChannelPipelinefinal EventLoopGroup currentChildGroup = childGroup;// 工作线程final ChannelHandler currentChildHandler = childHandler;// 用户自定义的 handlerfinal Entry<ChannelOption<?>, Object>[] currentChildOptions;final Entry<AttributeKey<?>, Object>[] currentChildAttrs;...// 为 NioServerSocketChannel的管道 ChannelPipeline 添加handler之 ChannelInitializerp.addLast(new ChannelInitializer<Channel>() {// initChannel初始化方法只会被调用一次//ChannelInitializer实例是被所有的Channel共享的,用于初始化ChannelPipeline//通过Set集合保存已经初始化的ChannelPipeline,避免重复初始化同一ChannelPipeline@Overridepublic void initChannel(final Channel ch) throws Exception {// ch:NioServerSocketChannelfinal ChannelPipeline pipeline = ch.pipeline();//ServerBootstrapConfig中的ChannelHandler需要用户显式的制定ChannelHandler handler = config.handler();//启动过程中 ServerBootstrapConfig 没有初始化ChannelHandlerif (handler != null) {pipeline.addLast(handler);}// 初始化器的职责是将 ServerBootstrapAcceptor 加入至 NioServerSocketChannelch.eventLoop().execute(new Runnable() {@Overridepublic void run() {pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));}});}});
}
  • 服务端ChannelPipeline中添加ChannelHandler此时只是初始化延迟任务【PendingHandlerAddedTask】。
  • 真正【隐式、显式的ChannelHandler】添加动作包括回调ChannelHandler默认方法#handlerAdded是NioServerSocketChannel完成注册功能后通过如下方法【AbstractChannel#register0{invokeHandlerAddedIfNeeded}】完成的。
    PendingHandlerAddedTask参考#3.1

NioServerSocketChannel的管道之 ChannelPipeline中 添加的handler之 ChannelInitializer只有ServerBootstrapAcceptor。区别于serverBootstrap.childHandler是针对客户端SocketChannel添加对应的ChannelInitializer。

2、Selector注册NioServerSocketChannel套接字任务

NioServerSocketChannel的注册功能首先会作为普通任务添加到队列。

public final void register(EventLoop eventLoop, final ChannelPromise promise) {// eventLoop:bossGroup的线程循环组// 一些检查,略.../*** 执行channel注册的操作必须是Reactor线程来完成* 1: 如果当前执行线程是Reactor线程,则直接执行register0进行注册* 2:如果当前执行线程是外部线程,则需要将register0注册操作 封装程异步Task 由Reactor线程执行* */AbstractChannel.this.eventLoop = eventLoop;// bossGroup if (eventLoop.inEventLoop()) {// 首次执行是main线程,并非Reactor线程register0(promise);} else {// 首次执行 execute 方法时,会启动 nio 线程,之后注册等操作在 nio 线程上执行// 因为只有一个 NioServerSocketChannel 因此,也只会有一个 boss nio 线程// 这行代码完成的事实是 main -> nio boss 线程的切换eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);//将 注册功能 封装为任务类型}});}
}

执行Channel向Reactor注册的动作必须要确保是在Reactor线程中执行。

  1. 如果当前线程是Reactor线程则直接执行注册动作register0。
  2. 如果当前线程不是Reactor线程,则需要将注册动作register0封装成异步任务,存放在Reactor中的taskQueue中,等待Reactor线程执行。

此时普通任务队列中只有NSSC在Selector上注册的功能【register0】

2.1、NSSC注册功能作为Runnable添加到普通队列

public void execute(Runnable task) {boolean inEventLoop = inEventLoop();// 添加任务,其中队列使用了 jctools 提供的 mpsc 无锁队列addTask(task);if (!inEventLoop) {//此时还是主线程// inEventLoop 如果为 false 表示由其它线程来调用 execute,即首次调用,这时需要向 eventLoop 提交首个任务,启动死循环,会执行到下面的 doStartThreadstartThread();// --> doStartThread();if (isShutdown()) {// 如果已经 shutdown,做拒绝逻辑,代码略...}}if (!addTaskWakesUp && wakesUpForTask(task)) {// 如果线程由于 IO select 阻塞了,添加的任务的线程需要负责唤醒 NioEventLoop 线程wakeup(inEventLoop);// 唤醒 select 阻塞线程`ioty.channel.nio.NioEventLoop#wakeup`}
}

2.2、Boss Group线程触发事件循环组执行

private void doStartThread() {executor.execute(new Runnable() {// 此时还是main线程@Overridepublic void run() {//该任务的执行是main线程触发Boss Group线程异步执行的thread = Thread.currentThread();// 该线程 就是1.2.2中Boss Group中新建的线程if (interrupted) {thread.interrupt();}boolean success = false;updateLastExecutionTime();// 执行队列中的任务SingleThreadEventExecutor.this.run();success = true;}});
}

Reactor线程创建参考#2.5

注意:步骤1对应的任务整个过程都是同步执行,因为只有BossLoopGroup中主线程驱动运行的。该线程的作用主要是驱动SingleThreadEventExecutor中的死循环,不断监听获取普通任务、IO事件任务。

3、SingleThreadEventExecutor处理队列中存在的任务–事件循环组

通过第二章#2.2得知,事件循环组通过BossLoopGroup中线程触发执行。事件循环组的主要任务就是执行普通任务、IO事件任务。

final class DefaultSelectStrategy implements SelectStrategy {static final SelectStrategy INSTANCE = new DefaultSelectStrategy();private DefaultSelectStrategy() { }@Overridepublic int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {// 如果存在任务,则返回 selector 注册的事件个数,否则返回-1,将增加从selector获取事件的超时时间,避免CPU的空轮转return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;}
}
public final class NioEventLoop extends SingleThreadEventLoop {private final IntSupplier selectNowSupplier = new IntSupplier() {@Overridepublic int get() throws Exception {return selectNow();//此处是以非阻塞方式返回selector 注册的IO事件个数 0 or n。}};protected void run() {for (;;) {try {int strategy;//0表示selector 没有注册的IO事件try {// calculateStrategy 的逻辑如下:// 有任务,会执行一次 selectNow,清除上一次的 wakeup 结果,无论有没有 IO 事件,都会跳过 switch// 没有任务,会匹配 SelectStrategy.SELECT,看是否应当阻塞strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());switch (strategy) {case SelectStrategy.CONTINUE:// -2continue;//是指IO循环轮询【非阻塞方式】新事件。这种策略只有在epoll模式下才支持,NIO和Kqueue模式并不支持这个策略case SelectStrategy.BUSY_WAIT://-3case SelectStrategy.SELECT://-1 通过SELECT【存在超时时间策略】阻塞获取SelectKeys注册的IO事件// 因为 IO 线程和提交任务线程都有可能执行 wakeup,而 wakeup 属于比较昂贵的操作,因此使用了一个原子布尔对象 	// wakenUp,它取值为 true 时,表示该由当前线程唤醒// 进行 select 阻塞,并设置唤醒状态为 falseboolean oldWakenUp = wakenUp.getAndSet(false);// 如果在这个位置,非 EventLoop 线程抢先将 wakenUp 置为 true,并 wakeup// 下面的 select 方法不会阻塞// 等 runAllTasks 处理完成后,到再循环进来这个阶段新增的任务会不会及时执行呢?// 因为 oldWakenUp 为 true,因此下面的 select 方法就会阻塞,直到超时// 才能执行,让 select 方法无谓阻塞select(oldWakenUp);default:}} final int ioRatio = this.ioRatio;//50cancelledKeys = 0;needsToSelectAgain = false;if (ioRatio == 100) {try {if (strategy > 0) {processSelectedKeys();}} finally {// Ensure we always run tasks.ranTasks = runAllTasks();}} else if (strategy > 0) {// selector 存在IO事件,则优先处理IO事件final long ioStartTime = System.nanoTime();try {processSelectedKeys();} finally {// 记录 io 事件处理耗时final long ioTime = System.nanoTime() - ioStartTime;// 运行非 IO 任务,一旦超时会退出 runAllTasksranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);}} else {ranTasks = runAllTasks(0); // 执行任务队列中的任务【register0】}} }}
}

如果strategy策略存在则表明存在IO事件任务,否则处理普通任务【例如,服务端启动过程中NioServerSocketChannel在Selector上注册的任务】。

strategy为-1表示IO事件以及普通队列任务均没有。0表示普通队列存在任务但是没有IO事件。n表示返回的IO事件个数。

这个方法通常只有Boss Group Reactor单线程执行,并且整个过程就是同步的。例如执行runAllTasks中的任务线程就是Reactor线程。观察该方法发现基本是个死循环,即Reactor线程持续不断地工作。

3.1、执行队列中普通任务

对于Server端来说核心是处理NSSC 在Selector上的注册功能。

protected boolean runAllTasks(long timeoutNanos) {fetchFromScheduledTaskQueue();Runnable task = pollTask();final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;long runTasks = 0;long lastExecutionTime;for (;;) {// Boss Group Reactor单线程不断循环执行safeExecute(task);// 通过循环执行队列中全部任务Runnable#runrunTasks ++;...task = pollTask();if (task == null) {lastExecutionTime = ScheduledFutureTask.nanoTime();break;}}afterRunningAllTasks();this.lastExecutionTime = lastExecutionTime;return true;
}

4、NSSC 在Selector上开始注册

注册任务主要包含两个核心任务的处理: 端口绑定任务、隐式添加handler之ServerBootstrapAcceptor任务。

register0是驱动整个Channel注册绑定流程的关键方法,下面我们来看下它的核心逻辑:

public abstract class AbstractChannel{private void register0(ChannelPromise promise) {try {if (!promise.setUncancellable() || !ensureOpen(promise)) {return;}boolean firstRegistration = neverRegistered;//执行真正的注册操作。原生的 nio channel 绑定到 selector 上,注意此时没有注册 selector 关注事件,附件为 NioServerSocketChanneldoRegister();neverRegistered = false;registered = true;// 回调pipeline中添加的ChannelInitializer的handlerAdded方法,在这里初始化channelPipelinepipeline.invokeHandlerAddedIfNeeded();//#1//设置regFuture为success,触发operationComplete回调,将ioty.bootstrap.AbstractBootstrap#doBind0操作放入Reactor的任务队列中,等待Reactor线程执行。safeSetSuccess(promise);//#2//触发channelRegister事件pipeline.fireChannelRegistered();//#3//对于服务端ServerSocketChannel来说 只有绑定端口地址成功后 channel的状态才是active的。//此时绑定操作作为异步任务在Reactor的任务队列中,绑定操作还没开始,所以这里的isActive()是false// 对应 server socket channel 还未绑定,isActive 为 falseif (isActive()) {if (firstRegistration) {//触发channelActive事件pipeline.fireChannelActive();} else if (config().isAutoRead()) {beginRead();}}} }
}
  1. 步骤1:pipeline.invokeHandlerAddedIfNeeded()方法触发回调ServerBootstrap#init#initChannel方法,将隐式添加handler之ServerBootstrapAcceptor的功能作为普通任务添加到队列中。最终将该handler添加到NioServerSocketChannel中管道DefaultChannelPipeline中。
  2. 步骤2:设置regFuture为Success,并回调注册在regFuture上的AbstractBootstrap#ChannelFutureListener#operationComplete方法,在operationComplete回调方法中将【绑定端口】操作封装成异步任务,提交到Reactor的taskQueue中。
  3. 步骤3:通过pipeline.fireChannelRegistered()在pipeline中触发channelRegister事件。pipeline中channelHandler的channelRegistered方法被回调。
  4. 对于Netty服务端NioServerSocketChannel来说, 只有绑定端口地址成功后 channel的状态才是active的。此时绑定操作在regFuture上注册的ChannelFutureListener#operationComplete回调方法中被作为异步任务提交到了Reactor的任务队列中,Reactor线程还没开始执行绑定任务。所以这里的isActive()是false。
  5. 当Reactor线程执行完register0方法后,才会去执行绑定任务。

以上步骤都是由boss reactor单线程执行的。所有的普通任务添加队列之后,由boss reactor单线程继续从队首依次消费处理每个普通任务。

protected void doRegister() throws Exception {//channel注册到Selector后获得的SelectKeyvolatile SelectionKey selectionKey;for (;;) {//拿到java原生的ServerSocketChannel,并注册到 selector上//0:表示没有关注事件// javaChannel() 获取服务端真正的套接字 ServerSocketChannelImplselectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);return;}
}

调用底层JDK NIO Channel方法java.nio.channels.SelectableChannel#register(java.nio.channels.Selector, int, java.lang.Object),将NettyNioServerSocketChannel中包装的JDK NIO ServerSocketChannel注册到Reactor中的JDK NIO Selector上。

public boolean isActive() {// 只有绑定端口地址成功后 channel的状态才是active的return isOpen() && javaChannel().socket().isBound();
}

简单介绍下SelectableChannel#register方法参数的含义:

  • Selector:表示JDK NIO Channel将要向哪个Selector进行注册。
  • int ops:表示Channel上感兴趣的IO事件,当对应的IO事件就绪时,Selector会返回Channel对应的SelectionKey。SelectionKey可以理解为Channel在Selector上的特殊表示形式,SelectionKey中封装了Channel感兴趣的IO事件集合【 ~ interestOps】,以及IO就绪的事件集合【~~readyOps】,同时也封装了对应的JDK NIOChannel以及注册的Selector。最后还有一个重要的属性attachment,可以允许我们在SelectionKey上附加一些自定义的对象。
  • Object attachment:向SelectionKey中添加用户自定义的附加对象。

这里NioServerSocketChannel向Reactor中的Selector注册的IO事件为0,这个操作的主要目的是先获取到Channel在Selector中对应的SelectionKey,完成注册。当绑定操作完成后,在去向SelectionKey添加感兴趣的IO事件【~OP_ACCEPT】事件。

同时通过SelectableChannel#register方法将Netty自定义的NioServerSocketChannel(这里的this指针)附着在SelectionKey的attechment属性上,完成Netty自定义Channel与JDK NIO Channel的关系绑定。这样在每次对Selector进行IO就绪事件轮询时,Netty 都可以从 JDK NIO Selector返回的SelectionKey中获取到自定义的Channel对象(这里指的就是NioServerSocketChannel)。

以上主要完成的功能:利用 Boss Group中线程完成NSSC 在Selector上的注册。下一步关注事件的绑定参考文章:事件关注

5、NioServerSocketChannel关注Accept事件

由章节4得知,boss reactor单线程处理端口绑定任务。

public abstract class AbstractBootstrap{private static void doBind0(ChannelFuture regFuture,Channel channel,SocketAddress localAddress, ChannelPromise promise){channel.eventLoop().execute(new Runnable() {@Overridepublic void run() {if (regFuture.isSuccess()) {//此时为truechannel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {promise.setFailure(regFuture.cause());}}});}
}
public class DefaultChannelPipeline implements ChannelPipeline {final class HeadContext extends AbstractChannelHandlerContextimplements ChannelOutboundHandler, ChannelInboundHandler {public void channelActive(ChannelHandlerContext ctx) {ctx.fireChannelActive();readIfIsAutoRead();// selectionKey 注册关注的Accept事件}}
}

NioServerSocketChannel只存在Accept事件,即只负责获取客户端的连接NioSocketChannel。之后会通过ServerBootstrapAcceptor将NioSocketChannel注册在work reactor线程中的SelectedKey之上,并关注read事件。如果客户端发送IO事件,则由work reactor线程对应的事件循环组监听该IO事件,最终通过NioSocketChannel绑定的各种handler从头部 -> 尾部依次处理。

6、NioSocketChannel关注Read事件

当客户端在关注connect事件之前,首先会服务端通过TCP协议建立连接,此时服务端boss Group Reactor线程监测到NioServerSocketChannel的SelcectedKey存在对应的IO事件。此时服务端需要在获取的NioSocketChannel上面关注read事件。

public final class NioEventLoop extends SingleThreadEventLoop {private void processSelectedKeys() {if (selectedKeys != null) {processSelectedKeysOptimized();} else {processSelectedKeysPlain(selector.selectedKeys());}}private void processSelectedKeysOptimized() {for (int i = 0; i < selectedKeys.size; ++i) {final SelectionKey k = selectedKeys.keys[i];selectedKeys.keys[i] = null;final Object a = k.attachment();//获取到NioServerSocketChannelif (a instanceof AbstractNioChannel) {processSelectedKey(k, (AbstractNioChannel) a);} else {....}...}}private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();...int readyOps = k.readyOps();//客户端connect事件,对应服务端触发了Accept事件。readyOps = 16...if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {//触发 NioServerSocketChannel 的Accept事件unsafe.read();// 触发执行read事件关注逻辑  AbstractNioMessageChannel#read}}
}
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {private final class NioMessageUnsafe extends AbstractNioUnsafe {public void read() {...int size = readBuf.size();for (int i = 0; i < size; i ++) {readPending = false;pipeline.fireChannelRead(readBuf.get(i));//执行ServerBootstrapAcceptor#channelRead}readBuf.clear();allocHandle.readComplete();pipeline.fireChannelReadComplete();} }}
}

6.1、ServerBootstrapAcceptor完成线程切换

public void channelRead(ChannelHandlerContext ctx, Object msg) {final Channel child = (Channel) msg;//NioSocketChannelchild.pipeline().addLast(childHandler);// 将用户自定义的childHandler 添加到NioSocketChannel对应的管道pipeline中setChannelOptions(child, childOptions, logger);setAttributes(child, childAttrs);childGroup.register(child).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (!future.isSuccess()) {forceClose(child, future.cause());}}});}

此时利用ServerBootstrapAcceptor完成Boss Group Reactor与Work Group Reactor线程之间的切换。

register:跟服务端启动流程中NioServerSocketChannel在Selector上注册逻辑基本一致。此处是将NioSocketChannel在某个Work Group Reactor线程的Selector上的注册作为普通任务添加到普通队列中。

截止当前,服务端存在两个事件循环组,即Boss Group Reactor、Work Group Reactor线程分别维护着属于当前的线程的事件循环组。其中Boss Group Reactor事件循环组关注的IO事件是客户端对应的Accept事件,Work Group Reactor事件循环组关注的是Accept事件之后的客户端read事件。此处也证明了Boss Group线程只处理客户端连接,Work Group线程处理具体业务逻辑。

6.2、关注Read事件

public abstract class AbstractChannel{private void register0(ChannelPromise promise) {//此时的线程为 Work Group Reactor线程...boolean firstRegistration = neverRegistered;doRegister();//NioServerSocket在对应selector上的注册,返回selection keyneverRegistered = false;registered = true;pipeline.invokeHandlerAddedIfNeeded();// 真正将用户自定义的childHandler 添加到 NioSocketChannel 对应的管道pipeline中safeSetSuccess(promise);// 对于NioSocketChannel,此时主要是标识为 激活 状态//NioServerSocketChannel、NioSocketChannel分别在Selector上注册过程中该方法好像都是空实现pipeline.fireChannelRegistered();if (isActive()) {// 通过 safeSetSuccess 标识 为trueif (firstRegistration) {pipeline.fireChannelActive();//在 NioSocketChannel 对应的selectionKey上关注 read事件} else if (config().isAutoRead()) {beginRead();}} }
}

至此,对于某个客户端NioSocketChannel已经在Server端完成该Socket的read事件关注。只要客户端利用该Socket发送数据,则服务端的Work Reactor线程对应的事件循环组将监听到对应的IO事件,交给对应NioSocketChannel中pipeline管道处理【从头 -> 尾方向顺序依次经过每个handler处理】。

更多推荐

1、Netty之Server端注册Accept事件Read事件的关注

本文发布于:2024-03-10 13:56:11,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1728167.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:事件   Server   Netty   Read   Accept

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!