Netty 中的消息解析和编解码器 (2)

所有的入站事件都实现了 ChannelInboundHandler 接口,不难理解我们的 handler 就是这样接收到 bytebuf 然后进行下一步处理的。

2. Read 事件一次可以读多少字节

说编解码器之前我们先解决一个问题,如果不使用任何的编解码器,默认的传输对象应该是 byteBuf,那么 Netty 默认一次是读取多少字节呢?前面在讲粘包的文章里我在 packageEvent1工程示例中演示了不使用任何编解码工具读取数据,默认一次会话会读取1024字节,大家有兴趣可以回到上一篇看看 Netty 中的粘包和拆包,在 handler 中打上断点就知道当前一次读取包的长度。既然知道是1024,就好奇到底是在哪里设置的,出发点肯定还是上面提到的 read() 方法:

byteBuf = allocHandle.allocate(allocator);

这一句就是从内存中拿出字节分配到 bytebuf,allocate() 是 RecvByteBufAllocator 接口中的方法,这个接口有很多实现类,那到底默认是哪个实现类生效呢?

我们再回到 NioSocetChannel ,看他的构造方法:

public NioSocketChannel(Channel parent, SocketChannel socket) { super(parent, socket); config = new NioSocketChannelConfig(this, socket.socket()); } private final class NioSocketChannelConfig extends DefaultSocketChannelConfig { private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) { super(channel, javaSocket); } @Override protected void autoReadCleared() { clearReadPending(); } }

这里会生成一些配置信息,主要是一些 socket 默认参数以供初始化连接使用。NioSocketChannelConfig 构造方法里面调用了父类 DefaultSocketChannelConfig 的构造方法:

public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) { super(channel); if (javaSocket == null) { throw new NullPointerException("javaSocket"); } this.javaSocket = javaSocket; // Enable TCP_NODELAY by default if possible. if (PlatformDependent.canEnableTcpNoDelayByDefault()) { try { setTcpNoDelay(true); } catch (Exception e) { // Ignore. } } }

同样这里又往上调用了父类 DefaultChannelConfig :

public DefaultChannelConfig(Channel channel) { this(channel, new AdaptiveRecvByteBufAllocator()); } protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) { setRecvByteBufAllocator(allocator, channel.metadata()); this.channel = channel; }

怎样,是不是看到了 AdaptiveRecvByteBufAllocator, 他就是 RecvByteBufAllocator 的实现类之一。所以我们只要看它是怎样设置默认值即可。

AdaptiveRecvByteBufAllocator 的默认构造方法:

public AdaptiveRecvByteBufAllocator() { this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM); }

这3个参数的默认值为:

static final int DEFAULT_MINIMUM = 64; static final int DEFAULT_INITIAL = 1024; static final int DEFAULT_MAXIMUM = 65536;

DEFAULT_MINIMUM 是缓冲区最小值,DEFAULT_INITIAL 是缓冲区默认值,DEFAULT_MAXIMUM是缓冲区最大值,到这里我们就找到了默认值是从哪里来的了。

默认大小是1024,但是并不是固定不变,它会有一个动态调整的动作。除了这三个字段外,还定义了两个动态调整容量的步长索引参数:

private static final int INDEX_INCREMENT = 4; private static final int INDEX_DECREMENT = 1;

扩张的步进索引为4,收缩的步进索引为1。

private static final int[] SIZE_TABLE; static { List<Integer> sizeTable = new ArrayList<Integer>(); for (int i = 16; i < 512; i += 16) { sizeTable.add(i); } for (int i = 512; i > 0; i <<= 1) { sizeTable.add(i); } SIZE_TABLE = new int[sizeTable.size()]; for (int i = 0; i < SIZE_TABLE.length; i ++) { SIZE_TABLE[i] = sizeTable.get(i); } }

SIZE_TABLE 为长度向量表,作用就是保存步长。上面的 static 修饰的代码块作用就是初始化长度向量表。从16开始,每次递增16,直到512,这里数组的下标为30。下标31的初始值为512, i递增的值为左移一位,左移一位相当于乘以2,所以每次递增是以当前值的倍数增加的,最终增加到的值直到 Integer 能达到的最大值。

长度向量表的值可以得出:

0-->16 1-->32 2-->48 3-->64 4-->80 5-->96 6-->112 7-->128 8-->144 9-->160 10-->176 11-->192 12-->208 13-->224 14-->240 15-->256 16-->272 17-->288 18-->304 19-->320 20-->336 21-->352 22-->368 23-->384 24-->400 25-->416 26-->432 27-->448 28-->464 29-->480 30-->496 31-->512 32-->1024 33-->2048 34-->4096 35-->8192 36-->16384 37-->32768 38-->65536 39-->131072 40-->262144 41-->524288 42-->1048576 43-->2097152 44-->4194304 45-->8388608 46-->16777216 47-->33554432 48-->67108864 49-->134217728 50-->268435456 51-->536870912 52-->1073741824

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzjyz.html