Netty分布式NioSocketChannel注册到selector方法解析
我们回到最初的niomessageunsafe的read()方法:
public void read() { //必须是nioeventloop方法调用的, 不能通过外部线程调用 assert eventloop().ineventloop(); //服务端channel的config final channelconfig config = config(); //服务端channel的pipeline final channelpipeline pipeline = pipeline(); //处理服务端接入的速率 final recvbytebufallocator.handle allochandle = unsafe().recvbufallochandle(); //设置配置 allochandle.reset(config); boolean closed = false; throwable exception = null; try { try { do { //创建jdk底层的channel //readbuf用于临时承载读到链接 int localread = doreadmessages(readbuf); if (localread == 0) { break; } if (localread < 0) { closed = true; break; } //分配器将读到的链接进行计数 allochandle.incmessagesread(localread); //连接数是否超过最大值 } while (allochandle.continuereading()); } catch (throwable t) { exception = t; } int size = readbuf.size(); //遍历每一条客户端连接 for (int i = 0; i < size; i ++) { readpending = false; //传递事件, 将创建niosokectchannel进行传递 //最终会调用serverbootstrap的内部类serverbootstrapacceptor的channelread()方法 pipeline.firechannelread(readbuf.get(i)); } readbuf.clear(); allochandle.readcomplete(); pipeline.firechannelreadcomplete(); //代码省略 } finally { //代码省略 } }
在while循环结束之后, 将会通过一个for循环遍历readbuf集合, 并将创建的niosocketchannel传入firechannelread()中, 传播channel的读取事件
有关pipeline的知识, 我们下一章会详细剖析, 并会根据剖析后的内容回顾之前的有关pipeline的操作, 这里我们只需知道, 通过firechannelread()我们最终调用了serverbootstrap的内部类serverbootstrapacceptor 中的channelread()方法
跟到channelread()方法中:
public void channelread(channelhandlercontext ctx, object msg) { final channel child = (channel) msg; //代码省略 try { //work线程注册channel childgroup.register(child).addlistener(new channelfuturelistener() { @override public void operationcomplete(channelfuture future) throws exception { if (!future.issuccess()) { forceclose(child, future.cause()); } } }); } catch (throwable t) { forceclose(child, t); } }
其中参数的msg就是最初传入firechannelread()方法的niosocketchannel
所以这里可以通过 final channel child = (channel) msg 这种方式拿到niosocketchannel
其中childgroup是我们最初初始化的work线程, 这里的register()方法跟boss线程一样, 通过next()方法获选择一个线程进行注册, 这里不再赘述
我们紧跟调用链, 跟到singlethreadeventloop的register()方法:
public channelfuture register(final channelpromise promise) { objectutil.checknotnull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }
这里的unsafe(), 根据我们之前的剖析, 是niobyteunsafe, 这里的register最终会调用abstractunsafe的register()方法, 并niosocketchannel
不知道同学们是否记得, 当初nioserversocketchannel注册的时候也走的这个方法
我们跟到register()这个方法中:
public final void register(eventloop eventloop, final channelpromise promise) { //省略验证代码 //所有的复制操作, 都交给eventloop处理 abstractchannel.this.eventloop = eventloop; if (eventloop.ineventloop()) { //做实际主注册 register0(promise); } else { try { eventloop.execute(new runnable() { @override public void run() { register0(promise); } }); } catch (throwable t) { //代码省略 } } }
我们学习过nioeventloop相关知识之后, 应该对这部分代码不太陌生, 首先判断是不是当前nioeventloop线程, 如果是, 则直接进行注册操作, 如果不是, 则封装成task在当前nioeventloop中执行
走到这里不难明白, 这里并不是当前nioeventloop线程, 这是boss线程执行的, 所以这里会走到else, 如果是第一次的连接操作, work线程的nioeventloop并没有启动, 所以这里也会启动nioeventloop, 并开始轮询操作
跟到register0(promise)中看其是如何做实际操作的:
private void register0(channelpromise promise) { try { //省略代码 //做实际的注册 doregister(); neverregistered = false; registered = true; //触发事件 pipeline.invokehandleraddedifneeded(); safesetsuccess(promise); //触发注册成功事件 pipeline.firechannelregistered(); if (isactive()) { if (firstregistration) { //传播active事件(4) pipeline.firechannelactive(); } else if (config().isautoread()) { beginread(); } } } catch (throwable t) { //省略代码 } }
这段代码我们同样并不陌生, 因为nioserversokectchannel中也走这一部分, 我们继续关注doregister()方法:
protected void doregister() throws exception { boolean selected = false; for (;;) { try { //jdk底层的注册方法 //第一个参数为selector, 第二个参数表示不关心任何事件 selectionkey = javachannel().register(eventloop().selector, 0, this); return; } catch (cancelledkeyexception e) { //省略代码 } } }
这部分也是我们之前剖析过的jdk底层的注册, 只是不同的是, 这里的javachannel()是socketchanel而不是serversocketchannel
同样, 这里也是表示不关心任何事件, 只是在当前nioeventloop绑定的selector上注册
至此, niosocketchannel完成注册
以上就是netty代码跟踪niosocketchannel注册到selector的详细内容,更多关于niosocketchannel注册到selector的资料请关注萬仟网其它相关文章!
看完文章,还可以用支付宝扫描下面的二维码领取一个支付宝红包,目前可领1-88元不等
除了扫码可以领取之外,大家还可以(复制 720087999 打开✔支付宝✔去搜索, h`o`n.g.包哪里来,动动手指就能领)。
看下图所示是好多参与这次活动领取红包的朋友: