Netty解码器原理分析
以定长解码器为例,理清脉络。
NioEventLoop,run
@Override protected void run() { int selectCnt = 0; for (;;) { //..... else if (strategy > 0) { final long ioStartTime = System.nanoTime(); try { processSelectedKeys();//处理发生事件的channel } finally { // Ensure we always run tasks. final long ioTime = System.nanoTime() - ioStartTime; ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio); } } } }
NioEventLoop,processSelectedKeys
private void processSelectedKeys() { if (selectedKeys != null) { processSelectedKeysOptimized(); } else { processSelectedKeysPlain(selector.selectedKeys()); } }
NioEventLoop,processSelectedKeysOptimized
private void processSelectedKeysOptimized() { for (int i = 0; i < selectedKeys.size; ++i) {//循环处理keys final SelectionKey k = selectedKeys.keys[i]; // null out entry in the array to allow to have it GC'ed once the Channel close // See https://github.com/netty/netty/issues/2363 selectedKeys.keys[i] = null;//帮助GC final Object a = k.attachment(); if (a instanceof AbstractNioChannel) { processSelectedKey(k, (AbstractNioChannel) a);//处理channel }
//...
} }
NioEventLoop,processSelectedKey
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) { final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();try { if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { unsafe.read();//处理读事件,从nio channel中读出数据 } } }
AbstractNioByteChannel,read
@Override public final void read() {//在一个循环里面读取缓冲区里的数据,是为了在处理这次缓冲区里的数据的时候,又有数据到来,不用等下一次select try { do { byteBuf = allocHandle.allocate(allocator); allocHandle.lastBytesRead(doReadBytes(byteBuf));//从channel中读取数据到byteBuf //... pipeline.fireChannelRead(byteBuf);//往后传播byteBuf,开始进入链 byteBuf = null; } while (allocHandle.continueReading()); } }
DefaultChannelPipeline,fireChannelRead
@Override public final ChannelPipeline fireChannelRead(Object msg) { AbstractChannelHandlerContext.invokeChannelRead(head, msg);//从双向链表的head开始 return this; }
AbstractChannelHandlerContext,invokeChannelRead
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) { final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next); EventExecutor executor = next.executor(); if (executor.inEventLoop()) {//如果当前线程是netty io线程就直接读 next.invokeChannelRead(m); } else { executor.execute(new Runnable() {//否则封装成任务,加到任务队列里 @Override public void run() { next.invokeChannelRead(m); } }); } }
private void invokeChannelRead(Object msg) { if (invokeHandler()) {//如果当前handlerContext中的handler可以调用 try { ((ChannelInboundHandler) handler()).channelRead(this, msg); } catch (Throwable t) { notifyHandlerException(t); } } else { fireChannelRead(msg); } }
ByteToMessageDecoder,channelRead,开始重点分析
ByteBuf cumulation;//缓存上次没有处理完的半包信息
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof ByteBuf) {//如果传进来的msg是一个ByteBuf,那么对它进行解码 CodecOutputList out = CodecOutputList.newInstance();//用一个list去装载解析出来的对象 try { first = cumulation == null; /** * todo 如果上次有未处理完的半包信息,那么累积新的信息进行处理 */ cumulation = cumulator.cumulate(ctx.alloc(), first ? Unpooled.EMPTY_BUFFER : cumulation, (ByteBuf) msg); callDecode(ctx, cumulation, out);//对msg进行解码(会传播解码出的信息),仍然可能半包,半包时会累积在cumulation中 } catch (DecoderException e) { throw e; } catch (Exception e) { throw new DecoderException(e); } finally { if (cumulation != null && !cumulation.isReadable()) {//如果该已读完(完整解码完),那么就释放半包缓存 numReads = 0; cumulation.release(); cumulation = null; } else if (++ numReads >= discardAfterReads) {//如果可以抛弃一些字节 // We did enough reads already try to discard some bytes so we not risk to see a OOME. // See https://github.com/netty/netty/issues/4275 numReads = 0; discardSomeReadBytes(); } int size = out.size(); firedChannelRead |= out.insertSinceRecycled(); fireChannelRead(ctx, out, size);//往后传播out,如果是定长解码器,在callDecode中已经传播完成了,这个时候的size=0 out.recycle();//清空数组 } } else {//否则往后传播 ctx.fireChannelRead(msg); } }
ByteToMessageDecoder, fireChannelRead,传递给下一个HandlerContext
static void fireChannelRead(ChannelHandlerContext ctx, CodecOutputList msgs, int numElements) { for (int i = 0; i < numElements; i ++) { ctx.fireChannelRead(msgs.getUnsafe(i));//把outputList里的数据一个个地往后传播 } }
ByteToMessageDecode,callDecode
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { try { while (in.isReadable()) {//对ByteBuf循环读出来进行解析 int outSize = out.size();
//如果outList里有数据就直接先往后传播,在定长解码器中,似乎是每解码出一个后,就将解码后的对象加到out中,即每一次循环都会将out往后传播,而out中只有一个
if (outSize > 0) { fireChannelRead(ctx, out, outSize); out.clear();//一次全部传下去然后清空 if (ctx.isRemoved()) { break; } outSize = 0; } int oldInputLength = in.readableBytes();//记录解码前的可读字节数 /** * 这里会调用具体的解码器如定长解码器里的decode方法, * 方法里一次会解码如8字节,如果不够8字节就不从in里面读。 * 分析读半包的处理 */ decodeRemovalReentryProtection(ctx, in, out);
}
}
}
ByteToMessageDecode,decodeRemovalReentryProtection
final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { decodeState = STATE_CALLING_CHILD_DECODE; try { //解码,解析到的会把对象放到out里。抽象方法,调用如定长解码器的decode(),解码出一个对象放到out里面 decode(ctx, in, out); } finally { boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING; decodeState = STATE_INIT; if (removePending) {//如果当前handlerContext要被remove,那么就马上往后传播 fireChannelRead(ctx, out, out.size()); out.clear(); handlerRemoved(ctx); } } }
FixedLengthFrameDecoder, decode
@Override protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded);//读出一个帧之后加到out里面 } }
protected Object decode( @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception { if (in.readableBytes() < frameLength) {//如果不够一个帧,就返回null return null; } else { return in.readRetainedSlice(frameLength);//读出一个帧 } }