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有關的初始化過程剖析完成