要分析流程最直接就是通过源码断点调试分析了,我这里就直接在handler里的channelRead0里抛出异常,查看下执行的堆栈。
java.lang.Exception: xxxxxxxxxxxxxxxxxxxxxxxx
at com.test.MsgHandler.channelRead0(MsgHandler.java:26)
at com.test.MsgHandler.channelRead0(MsgHandler.java:1)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
at java.lang.Thread.run(Thread.java:745)
Netty 服务器端配置
public void initServer() throws Exception{
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder(CHARSET));
ch.pipeline().addLast(new StringEncoder(CHARSET));
ch.pipeline().addLast(new MsgHandler());
}
});
b.bind(9191).sync();
}
一个解码和编码 再加一个自定义的Handler 结合上面的异常堆栈来分析这次数据的流入过程
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run
从异常堆栈的最下面开始分析。这个类的源代码是
private static final class DefaultRunnableDecorator implements Runnable {
private final Runnable r;
DefaultRunnableDecorator(Runnable r) {
this.r = r;
}
@Override
public void run() {
try {
r.run();
} finally {
FastThreadLocal.removeAll();
}
}
}
可以看出这个类实现了Runnable接口,对Runnable做了一层封装。finally 执行对线程绑定的变量释放
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run
执行具体的业务逻辑是由线程来执行的 这里可以看出使用的是SingleThreadEventExecutor抽象类,
run方法的实现是由NioEventLoop来重写的,run方法里调用了processSelectedKeys();在调用
processSelectedKeysOptimized();这个方法里发现是Nio的连接 就走了 processSelectedKey(k, (AbstractNioChannel) a);方法
@Override
public final void read() {
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
byteBuf = allocHandle.allocate(allocator);
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
这里开始读取ByteBuf数据,调用 pipeline.fireChannelRead(byteBuf);方法抛出读取事件,这里就会开始从
pipeline里的头开始处理 一直到用户端的handler结束了。
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
DefaultChannelPipeline里记录了第一个ChannelHandlerContext head
一个handler 有一个对应的ChannelHandlerContext context 保留了一些变量。
ChannelHandlerContext 调用invokeChannelRead 之后
((ChannelInboundHandler) handler()).channelRead(this, msg);获取对应的handler来执行channelRead方法。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
}
这里可以看到head里没有啥处理直接把这个消息通过ChannelHandlerContext 继续往下传递。
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(), msg);
return this;
}
ChannelHandlerContext 就开始查找自己的下一个ChannelHandlerContext 。因为在in的链表上自己加了一个StringDecoder 所以这里应该执行到解码阶段
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
CodecOutputList out = CodecOutputList.newInstance();
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I cast = (I) msg;
try {
decode(ctx, cast, out);
} finally {
ReferenceCountUtil.release(cast);
}
} else {
out.add(msg);
}
} catch (DecoderException e) {
throw e;
} catch (Exception e) {
throw new DecoderException(e);
} finally {
int size = out.size();
for (int i = 0; i < size; i ++) {
ctx.fireChannelRead(out.getUnsafe(i));
}
out.recycle();
}
}
在这里调用子类实现的decode(ctx, cast, out);方法 解码成一个完整的自定义的对象String 然后通过fireChannelRead 抛出到下一个ChannelHandlerContext中去,下一个就是我们自定义的handler了。
@Override
protected void channelRead0(ChannelHandlerContext arg0, String str) throws Exception {
// TODO Auto-generated method stub
arg0.writeAndFlush(str).addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
// TODO Auto-generated method stub
if(future.isSuccess()){
System.out.println();
}
}
});
i++;
if(i >= 10000){
throw new Exception("xxxxxxxxxxxxxxxxxxxxxxxx");
}
}
这里就收到解码器传过来的完整对象。这里添加了一个GenericFutureListener ,Netty不推荐,也不希望使用同步调用Future的实现来等待结果的返回,因为这样对性能影响太大。所以调用operationComplete是由io线程处理完之后通过事件再主动通知回来。