前言
经过了服务启动,建立连接,接收数据,接下来就是要执行业务逻辑了,这一回,我们就来看看Netty是怎么操作业务逻辑的
开始
首先明确一点,操作业务逻辑的前序流程和接收数据其实是一样的,不同之处在于操作业务逻辑是通过fireChannelread方法传播出去之后开始处理,之前我们碰到了fireChannelread方法后都是去找到headContext看源码,其实fireChannelread的调用者是ChannelPipeline,而ChannelPipeline里又是由一个个的ChannelHandler组成,ChannelHandler也不是能直接执行的,而是由ChannelhannelHandlerContext来实现的。
从图上看,pipeline有两个流向,分别是从外向内的流向和从内向外的流向,对应的是读数据和写数据,又叫入站Inbound和出站Outbound,可以看到图中有一个个的处理器,注意,要理解Inbound和Outbound,不能用传统IO中的In代表输入,Out代表输出来看待,Inbound的含义是不是应用内部主动请求的事件,比如读取数据读取完这个事件,本身不是应用内部发起的,所以是InboundHandler,反过来读取数据本身这个操作就是一个OutBoundHandler。
ChannelPipeline在传播事件时,会判断ChannelPipeline中的下一个handler类型是不是和事件本身的运动方向相匹配,如果不匹配则会跳过该handler前进到下一个,在这里我们需要处理业务逻辑需要实现的是InboundHandler。
接下来我们来看源码,在这里我们首先套用上一次讲接收数据的流程,把断点打在NioEventLoop的unsafe.read方法上,先启动server,再启动client,然后断点进入方法,一直执行到pipeline.fireChannelRead(readBuf.get(i));这个方法,继续下一步执行,到了io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead(io.netty.channel.AbstractChannelHandlerContext, java.lang.Object)
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
//这个executor就是NioEventLoop
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
//执行ChannelRead
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
再断点进入invokeChannelRead方法
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
//可以看到,这里出现了ChannelInboundHandler,并且在继续传播
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireChannelRead(msg);
}
}
继续往下走,走到io.netty.channel.AbstractChannelHandlerContext#fireChannelRead这个方法
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
//这里的findContextInbound方法,就是寻找下一个可以执行ChannelRead的处理器
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
继续
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.next;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));//这里判断了下一个Handler是不是能执行channelRead
return ctx;
}
于是返回到EchoServerHandler中,示例代码的EchoServerHandler确实是继承了ChannelInboundHandlerAdapter,也重写了ChannelRead方法,所以确实有资格处理
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//这是官方示例代码,实际应用中业务逻辑代码就写在这个地方
ctx.write(msg);
}
总结
总结一下,1.Netty中处理业务逻辑的本质就是在pipeline中所有handler的ChannelRead的执行过程,2.默认处理线程是Channel绑定的NioEventLoop线程,也可以用pipeline.addLast来修改pipeline,3.handler的处理中途可以退出。