Netty源碼分析第3章(客戶端接入流程)---->第1節: 初始化NioSockectChannelConfig

 

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

 

上一節: 執行任務隊列

下一節: 處理接入事件之handle的建立

相關文章
相關標籤/搜索