Netty源码分析第三章: 客户端接入流程html
概述:java
以前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件以后是如何处理的?这一章咱们按部就班, 带你们继续剖析客户端接入以后的相关逻辑数组
第一节:初始化NioSockectChannelConfigsocket
在剖析接入流程以前咱们首先补充下第一章有关建立channel的知识:oop
咱们在第一章剖析过channel的建立, 其中NioServerSocketChannel中有个构造方法:源码分析
public NioServerSocketChannel(ServerSocketChannel channel) { super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); }
当时咱们并无剖析config相关知识, 在这一章首先对此作一个补充, 这里咱们看到每个NioServerSocketChannel都拥有一个config属性, 这个属性存放着NioServerSocketChannel的相关配置, 这里建立一个NioServerSocketChannelConfig对象, 并将当前channel, 和channel对应的java底层的socket对象进行了传入, NioServerSocketChannelConfig实际上是NioServerSocketChannel的内部类学习
咱们跟到NioServerSocketChannelConfig类的构造方法中:this
private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) { super(channel, javaSocket); }
咱们继续跟入其父类DefaultServerSocketChannelConfig的构造方法中:spa
public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) { super(channel); if (javaSocket == null) { throw new NullPointerException("javaSocket"); } this.javaSocket = javaSocket; }
这里继续调用了其父类的构造方法, 并保存了jdk底层的socket对象, 而且调用其父类DefaultChannelConfig的构造方法netty
跟到其父类DefaultChannelConfig的构造方法中:
public DefaultChannelConfig(Channel channel) { this(channel, new AdaptiveRecvByteBufAllocator()); }
这里调用了自身的构造方法, 传入了channel和一个AdaptiveRecvByteBufAllocator对象
AdaptiveRecvByteBufAllocator是一个缓冲区分配器, 用于分配一个缓冲区Bytebuf的, 有关Bytebuf的相关内容会在后面的章节详细讲解, 这里能够简单介绍做为了解, 就当对于以后知识的预习
Bytebuf至关于jdk的ByetBuffer, Netty对其作了从新的封装, 用于读写channel中的字节流, 熟悉Nio的同窗对此应该并不陌生, AdaptiveRecvByteBufAllocator就是用于分配netty中ByetBuff的缓冲区分配器, 根据名字, 咱们不难看出这个缓冲区是一个可变大小的字节缓冲区
咱们跟到AdaptiveRecvByteBufAllocator的构造方法中:
public AdaptiveRecvByteBufAllocator() { //DEFAULT_MINIMUM:最小缓冲区长度64字节 //DEFAULT_INITIAL:初始容量1024字节 //最大容量65536字节
this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM); }
这里调用自身的构造方法而且传入了三个属性, 这三个属性的含义分别为:
DEFAULT_MINIMUM:表明要分配的缓冲区长度最少为64个字节
DEFAULT_INITIAL:表明要分配的缓冲区的初始容量为1024个字节
DEFAULT_MAXIMUM:表明要分配的缓冲区最大容量为65536个字节
咱们跟到this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM)方法中
public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) { //忽略验证代码 //最小容量在table中的下标
int minIndex = getSizeTableIndex(minimum); if (SIZE_TABLE[minIndex] < minimum) { this.minIndex = minIndex + 1; } else { this.minIndex = minIndex; } //最大容量在table中的下标
int maxIndex = getSizeTableIndex(maximum); if (SIZE_TABLE[maxIndex] > maximum) { this.maxIndex = maxIndex - 1; } else { this.maxIndex = maxIndex; } this.initial = initial; }
其中这里初始化了三个属性, 分别是:
minIndex:最小容量在size_table中的下标
maxIndex:最大容量在table中的下标
initial:初始容量1024个字节
这里的size_table就是一个数组, 里面盛放着byteBuf可分配的内存大小的集合, 分配的bytebuf不管是扩容仍是收缩, 内存大小都属于size_table中的元素, 那么这个数组是如何初始化的, 咱们跟到这个属性中:
private static final int[] SIZE_TABLE;
咱们看到是一个final修饰的静态成员变量, 咱们跟到static块中看它的初始化过程:
static { //List集合
List<Integer> sizeTable = new ArrayList<Integer>(); //从16开始, 每递增16添加到List中, 直到大于等于512
for (int i = 16; i < 512; i += 16) { sizeTable.add(i); } //从512开始, 倍增添加到List中, 直到内存溢出
for (int i = 512; i > 0; i <<= 1) { sizeTable.add(i); } //初始化数组
SIZE_TABLE = new int[sizeTable.size()]; //将list的内容放入数组中
for (int i = 0; i < SIZE_TABLE.length; i ++) { SIZE_TABLE[i] = sizeTable.get(i); } }
首先建立一个Integer类型的list用于盛放内存元素
这里经过两组循环为list添加元素
首先看第一组循环:
for (int i = 16; i < 512; i += 16) { sizeTable.add(i); }
这里是经过16平移的方式, 直到512个字节, 将每次平移以后的内存大小添加到list中
再看第二组循环
for (int i = 512; i > 0; i <<= 1) { sizeTable.add(i); }
超过512以后, 再经过倍增的方式循环, 直到int类型内存溢出, 将每次倍增以后大小添加到list中
最后初始化SIZE_TABLE数组, 将list中的元素按下表存放到数组中
这样就初始化了内存数组
再回到AdaptiveRecvByteBufAllocator的构造方法中:
public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) { //忽略验证代码 //最小容量在table中的下标
int minIndex = getSizeTableIndex(minimum); if (SIZE_TABLE[minIndex] < minimum) { this.minIndex = minIndex + 1; } else { this.minIndex = minIndex; } //最大容量在table中的下标
int maxIndex = getSizeTableIndex(maximum); if (SIZE_TABLE[maxIndex] > maximum) { this.maxIndex = maxIndex - 1; } else { this.maxIndex = maxIndex; } this.initial = initial; }
这里分别根据传入的最小和最大容量去SIZE_TABLE中获取其下标
咱们跟到getSizeTableIndex(minimum)中:
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; } } }
这里是经过二分查找去获取其下表
if (SIZE_TABLE[minIndex] < minimum)这里判断最小容量下标所属的内存大小是否小于最小值, 若是小于最小值则下标+1
最大容量的下标获取原理同上, 判断最大容量下标所属内存大小是否大于最大值, 若是是则下标-1
咱们回到DefaultChannelConfig的构造方法:
public DefaultChannelConfig(Channel channel) { this(channel, new AdaptiveRecvByteBufAllocator()); }
刚才咱们剖析过了AdaptiveRecvByteBufAllocator()的建立过程, 咱们继续跟到this()中:
protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) { setRecvByteBufAllocator(allocator, channel.metadata()); this.channel = channel; }
咱们看到这里初始化了channel, 在channel初始化以前, 调用了setRecvByteBufAllocator(allocator, channel.metadata())方法, 顾名思义, 这是用于设置缓冲区分配器的方法, 第一个参数是咱们刚刚分析过的新建的AdaptiveRecvByteBufAllocator对象, 第二个传入的是与channel绑定的ChannelMetadata对象, ChannelMetadata对象是什么?
咱们跟进到metadata()方法当中, 因为是channel是NioServerSocketChannel, 因此调用到了NioServerSocketChannel的metadata()方法:
public ChannelMetadata metadata() { return METADATA; }
这里返回了一个成员变量METADATA, 跟到这个成员变量中:
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
这里建立了一个ChannelMetadata对象, 并在构造方法中传入false和16
继续跟到ChannelMetadata的构造方法中:
public ChannelMetadata(boolean hasDisconnect, int defaultMaxMessagesPerRead) { //省略验证代码 //false
this.hasDisconnect = hasDisconnect; //16
this.defaultMaxMessagesPerRead = defaultMaxMessagesPerRead; }
这里作的事情很是简单, 只初始化了两个属性:
hasDisconnect=false
defaultMaxMessagesPerRead=16
defaultMaxMessagesPerRead=16表明在读取对方的连接或者channel的字节流时(不管server仍是client), 最多只循环16次, 后面的讲解将会看到
剖析完了ChannelMetadata对象的建立, 咱们回到DefaultChannelConfig的构造方法:
protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) { setRecvByteBufAllocator(allocator, channel.metadata()); this.channel = channel; }
跟到setRecvByteBufAllocator(allocator, channel.metadata())方法中:
private void setRecvByteBufAllocator(RecvByteBufAllocator allocator, ChannelMetadata metadata) { if (allocator instanceof MaxMessagesRecvByteBufAllocator) { ((MaxMessagesRecvByteBufAllocator) allocator).maxMessagesPerRead(metadata.defaultMaxMessagesPerRead()); } else if (allocator == null) { throw new NullPointerException("allocator"); } rcvBufAllocator = allocator; }
首先会判断传入的缓冲区分配器是否是MaxMessagesRecvByteBufAllocator类型的, 由于AdaptiveRecvByteBufAllocator实现了MaxMessagesRecvByteBufAllocator接口, 因此此条件成立
以后将其转换成MaxMessagesRecvByteBufAllocator类型, 而后调用其maxMessagesPerRead(metadata.defaultMaxMessagesPerRead())方法, 这里会走到其子类DefaultMaxMessagesRecvByteBufAllocator的maxMessagesPerRead(int maxMessagesPerRead)方法中, 其中参数metadata.defaultMaxMessagesPerRead()返回就是ChannelMetadata的属性defaultMaxMessagesPerRead, 也就是16
跟到maxMessagesPerRead(int maxMessagesPerRead)方法中:
public MaxMessagesRecvByteBufAllocator maxMessagesPerRead(int maxMessagesPerRead) { //忽略验证代码 //初始化为16
this.maxMessagesPerRead = maxMessagesPerRead; return this; }
这里将自身属性maxMessagesPerRead设置为16, 而后返回自身
回到DefaultChannelConfig的构造方法:
private void setRecvByteBufAllocator(RecvByteBufAllocator allocator, ChannelMetadata metadata) { if (allocator instanceof MaxMessagesRecvByteBufAllocator) { ((MaxMessagesRecvByteBufAllocator) allocator).maxMessagesPerRead(metadata.defaultMaxMessagesPerRead()); } else if (allocator == null) { throw new NullPointerException("allocator"); } rcvBufAllocator = allocator; }
设置完了内存分配器的maxMessagesPerRead属性, 最后将DefaultChannelConfig自身的成员变量rcvBufAllocator设置成咱们初始化完毕的allocator对象
至此, 有关channelConfig有关的初始化过程剖析完成