netty-read

本文深入剖析了Netty中AbstractNioByteChannel的read方法,详细解读了读操作的流程,包括分配缓冲区、读取数据、触发读事件、调整预分配大小等关键步骤,并解释了AdaptiveRecvByteBufAllocator如何智能调整读取大小。

AbstractNioByteChannel.read()

@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;
                        if (close) {
                            // There is nothing left to read as we received an EOF.
                            readPending = false;
                        }
                        break;
                    }

                    allocHandle.incMessagesRead(1);//每读一次,会加一次read
                    readPending = false;
                    pipeline.fireChannelRead(byteBuf);//触发事件
                    byteBuf = null;
                } while (allocHandle.continueReading());

                allocHandle.readComplete();//调用record()每次读完会进行,预分配调整,默认第一次是1024
                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();
                }
            }
 

DefaultMaxBytesRecvByteBufAllocator

   @Override
        public boolean continueReading() {
            return continueReading(defaultMaybeMoreSupplier);
        }

        @Override
        public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
            return config.isAutoRead() &&
                   maybeMoreDataSupplier.get() &&
                   totalMessages < maxMessagePerRead &&  //有判断读的次数
                   totalBytesRead > 0;
        }
     private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
            @Override
            public boolean get() {
                return attemptBytesRead == lastBytesRead;//默认分配了1024 只读了420,所以不继续读
            }
        };

AdaptiveRecvByteBufAllocator.HandleImpl计算每次需要的byteSize平均值

private final class HandleImpl extends MaxMessageHandle {
        private final int minIndex;
        private final int maxIndex;
        private int index;
        private int nextReceiveBufferSize;
        private boolean decreaseNow;

        public HandleImpl(int minIndex, int maxIndex, int initial) {
            this.minIndex = minIndex;
            this.maxIndex = maxIndex;

            index = getSizeTableIndex(initial);
            nextReceiveBufferSize = SIZE_TABLE[index];
        }

        @Override
        public int guess() {
            return nextReceiveBufferSize;
        }
//每次读完会进行,预分配调整,默认第一次是1024
        private void record(int actualReadBytes) {
            if (actualReadBytes <= SIZE_TABLE[Math.max(0, index - INDEX_DECREMENT - 1)]) {
                if (decreaseNow) {
                    index = Math.max(index - INDEX_DECREMENT, minIndex);
                    nextReceiveBufferSize = SIZE_TABLE[index];
                    decreaseNow = false;
                } else {
                    decreaseNow = true;
                }
            } else if (actualReadBytes >= nextReceiveBufferSize) {
                index = Math.min(index + INDEX_INCREMENT, maxIndex);
                nextReceiveBufferSize = SIZE_TABLE[index];
                decreaseNow = false;
            }
        }

        @Override
        public void readComplete() {
            record(totalBytesRead());
        }
    }

    private static int getSizeTableIndex(final int size) {
        for (int low = 0, high = SIZE_TABLE.length - 1;;) {
            if (high < low) {
                return low;
            }
            if (high == low) {
                return high;
            }

            int mid = low + high >>> 1;
            int a = SIZE_TABLE[mid];
            int b = SIZE_TABLE[mid + 1];
            if (size > b) {
                low = mid + 1;
            } else if (size < a) {
                high = mid - 1;
            } else if (size == a) {
                return mid;
            } else {
                return mid + 1;
            }
        }
    }

导读

AbstractNioByteChannel.read()
  AdaptiveRecvByteBufAllocator allocHandle = recvBufAllocHandle()
  byteBuf = allocHandle.allocate(allocator);
  allocHandle.lastBytesRead(doReadBytes(byteBuf));
  allocHandle.incMessagesRead(1);
  readPending = false;
  pipeline.fireChannelRead(byteBuf);
  allocHandle.continueReading()
    continueReading(defaultMaybeMoreSupplier)
    bytesToRead > 0 && maybeMoreDataSupplier.get()
	    UncheckedBooleanSupplier.get()
	      return attemptBytesRead == lastBytesRead
  
  allocHandle.readComplete();
     record(totalBytesRead());
       nextReceiveBufferSize = SIZE_TABLE[index]
  pipeline.fireChannelReadComplete();
  closeOnRead(pipeline)
    if (!isInputShutdown0()) {
    	if (Boolean.TRUE.equals(config().getOption(ChannelOption.ALLOW_HALF_CLOSURE))) {
	       shutdownInput();
	       pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
       }
    else
      doClose()
	      doClose0(promise);
	      outboundBuffer.failFlushed(cause, notify);
	      outboundBuffer.close(closeCause);
	      fireChannelInactiveAndDeregister(wasActive);
    pipeline.fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE)
  if (!readPending && !config.isAutoRead()) {
      removeReadOp();
  }
按你说的之后C:\Users\admin\.jdks\jdk-17.0.12+7\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2024.3.6\lib\idea_rt.jar=52853" -Dfile.encoding=UTF-8 -classpath D:\UserData\Desktop\Netty和gRPC\Netty_TEST\target\classes;D:\r\io\netty\netty-all\4.1.86.Final\netty-all-4.1.86.Final.jar;D:\r\io\netty\netty-buffer\4.1.86.Final\netty-buffer-4.1.86.Final.jar;D:\r\io\netty\netty-codec\4.1.86.Final\netty-codec-4.1.86.Final.jar;D:\r\io\netty\netty-codec-dns\4.1.86.Final\netty-codec-dns-4.1.86.Final.jar;D:\r\io\netty\netty-codec-haproxy\4.1.86.Final\netty-codec-haproxy-4.1.86.Final.jar;D:\r\io\netty\netty-codec-http\4.1.86.Final\netty-codec-http-4.1.86.Final.jar;D:\r\io\netty\netty-codec-http2\4.1.86.Final\netty-codec-http2-4.1.86.Final.jar;D:\r\io\netty\netty-codec-memcache\4.1.86.Final\netty-codec-memcache-4.1.86.Final.jar;D:\r\io\netty\netty-codec-mqtt\4.1.86.Final\netty-codec-mqtt-4.1.86.Final.jar;D:\r\io\netty\netty-codec-redis\4.1.86.Final\netty-codec-redis-4.1.86.Final.jar;D:\r\io\netty\netty-codec-smtp\4.1.86.Final\netty-codec-smtp-4.1.86.Final.jar;D:\r\io\netty\netty-codec-socks\4.1.86.Final\netty-codec-socks-4.1.86.Final.jar;D:\r\io\netty\netty-codec-stomp\4.1.86.Final\netty-codec-stomp-4.1.86.Final.jar;D:\r\io\netty\netty-codec-xml\4.1.86.Final\netty-codec-xml-4.1.86.Final.jar;D:\r\io\netty\netty-common\4.1.86.Final\netty-common-4.1.86.Final.jar;D:\r\io\netty\netty-handler\4.1.86.Final\netty-handler-4.1.86.Final.jar;D:\r\io\netty\netty-transport-native-unix-common\4.1.86.Final\netty-transport-native-unix-common-4.1.86.Final.jar;D:\r\io\netty\netty-handler-proxy\4.1.86.Final\netty-handler-proxy-4.1.86.Final.jar;D:\r\io\netty\netty-handler-ssl-ocsp\4.1.86.Final\netty-handler-ssl-ocsp-4.1.86.Final.jar;D:\r\io\netty\netty-resolver\4.1.86.Final\netty-resolver-4.1.86.Final.jar;D:\r\io\netty\netty-resolver-dns\4.1.86.Final\netty-resolver-dns-4.1.86.Final.jar;D:\r\io\netty\netty-transport\4.1.86.Final\netty-transport-4.1.86.Final.jar;D:\r\io\netty\netty-transport-rxtx\4.1.86.Final\netty-transport-rxtx-4.1.86.Final.jar;D:\r\io\netty\netty-transport-sctp\4.1.86.Final\netty-transport-sctp-4.1.86.Final.jar;D:\r\io\netty\netty-transport-udt\4.1.86.Final\netty-transport-udt-4.1.86.Final.jar;D:\r\io\netty\netty-transport-classes-epoll\4.1.86.Final\netty-transport-classes-epoll-4.1.86.Final.jar;D:\r\io\netty\netty-transport-classes-kqueue\4.1.86.Final\netty-transport-classes-kqueue-4.1.86.Final.jar;D:\r\io\netty\netty-resolver-dns-classes-macos\4.1.86.Final\netty-resolver-dns-classes-macos-4.1.86.Final.jar;D:\r\io\netty\netty-transport-native-epoll\4.1.86.Final\netty-transport-native-epoll-4.1.86.Final-linux-x86_64.jar;D:\r\io\netty\netty-transport-native-epoll\4.1.86.Final\netty-transport-native-epoll-4.1.86.Final-linux-aarch_64.jar;D:\r\io\netty\netty-transport-native-kqueue\4.1.86.Final\netty-transport-native-kqueue-4.1.86.Final-osx-x86_64.jar;D:\r\io\netty\netty-transport-native-kqueue\4.1.86.Final\netty-transport-native-kqueue-4.1.86.Final-osx-aarch_64.jar;D:\r\io\netty\netty-resolver-dns-native-macos\4.1.86.Final\netty-resolver-dns-native-macos-4.1.86.Final-osx-x86_64.jar;D:\r\io\netty\netty-resolver-dns-native-macos\4.1.86.Final\netty-resolver-dns-native-macos-4.1.86.Final-osx-aarch_64.jar;D:\r\com\fasterxml\jackson\core\jackson-databind\2.13.0\jackson-databind-2.13.0.jar;D:\r\com\fasterxml\jackson\core\jackson-annotations\2.13.0\jackson-annotations-2.13.0.jar;D:\r\com\fasterxml\jackson\core\jackson-core\2.13.0\jackson-core-2.13.0.jar org.example.GreetServer 8月 25, 2025 5:29:14 下午 io.netty.channel.DefaultChannelPipeline onUnhandledInboundException 警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception. com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens at [Source: (String)"\u0000\u0000\u0000\u001E{"name":"Alice","greeting":""}"; line: 1, column: 2] at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2391) at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:735) at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:713) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2462) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:698) at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4762) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4668) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3598) at org.example.GreetServerHandler.channelRead0(GreetServerHandler.java:26) at org.example.GreetServerHandler.channelRead0(GreetServerHandler.java:15) at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:840)
最新发布
08-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值