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);//读出一个帧
        }
    }

 

posted @ 2020-04-02 19:33  Allen没有青春  阅读(310)  评论(0编辑  收藏  举报