天天看點

netty 的流量整形讀測試

 先下結論:

當用戶端寫的速度比較小的時候, 即 用戶端寫的速度 <= 服務端 readLimit, 那麼, 用戶端 寫的速度就是 服務端讀的速度, 相當于是沒有限速,用戶端的寫不會被阻塞,用戶端也不會出現 isWritable = false的情況;(但用戶端每次讀的資料最大是65536,這個是 緩沖區限制導緻的。 ) 當用戶端寫的速度比較大的時候, 即 用戶端寫的速度 > 服務端 readLimit, 那麼, 實際速度 大于 服務端讀速度,小于用戶端寫的速度, 就是說 總資料量不變,時間不一樣!! 平均起來還是 讀的速度越大,寫的時候isWritable 為false 出現的越少! 細分兩個情況:     如果 用戶端寫的速度 < ChannelOption.SO_RCVBUF 選項值, 那麼,服務端以 ChannelOption.SO_RCVBUF * 5 為準 每N秒接收一次。同時,SO_RCVBUF 越大,N越小,也就是接收越快!     如果 用戶端寫的速度 > ChannelOption.SO_RCVBUF 選項值, 那麼,服務端 的接收速度可能小于 ChannelOption.SO_RCVBUF *5 。 需要注意的是,如果 對某一個配置, 同時設定了 option childOption, 那麼以 childOption 為準! 但如果childOption的設定 過小,則它無效,還是option 為準 ! SO_RCVBUF 的意思是, 即使用戶端寫的資料量、速度最大,服務端需要先經過SO_RCVBUF, SO_RCVBUF滿了之後,服務端立即觸發一次讀。 但是,ChannelOption.SO_RCVBUF * 5 不能超過 65536 KB,超過了就使用 65536 KB, 不知道為什麼, 可能是因為作業系統或者TCP協定本身限制了最大的緩沖! 測試很久才發現,沒法調整,開始還以為代碼錯了! 另外,用戶端 設定WriteBufferWaterMark 和 用戶端的 isWritable 有一定關系! 用戶端的 isWritable ,是一個比較奇怪的東西。 說白了,用戶端寫的時候, 是能夠感受到 服務端讀的壓力的,如果服務端讀不過來了(可能導緻TCP的buffer 也出現擁塞)比如服務端的接收緩沖區已經滿了,那麼寫也可能會阻塞,這個時候,用戶端的 isWritable 自動變成了false。 同時,用戶端也也有寫緩沖,也就是 ChannelOption.WRITE_BUFFER_WATER_MARK 選項 ( 可能包括ChannelOption.SO_SNDBUF, 沒試過),DEFAULT_HIGH_WATER_MARK 預設是 65535。 當然服務端讀出現了阻塞,那麼用戶端設定的high 越大,寫緩沖區就越大,isWritable = false 出現的越少! (因為寫滿了, 還是會出現 isWritable 自動變成false) 但是呢,用戶端的寫,也可以不用理 isWritable,這個時候,寫的資料會悉數發送到服務端( 沒觀察到 資料丢失的情況, 可能是緩存足夠大, ),

測試結果:

用戶端不考慮阻塞,發送資料 100 kb, 每次1 kb,1s 10次, 即用戶端寫速度是 10KB /s :服務端讀限制 1KB /s 。 服務端的 ChannelOption.SO_RCVBUF 分别設定如下 左邊數字, 接收完用戶端全部資料總花費時間為 中間的 秒數值 :  64 - 98 s 接收最大 320,實際 1024 KB, 符合理論。按理計算是花了 100, --- 為什麼不是 100s ? 大概因為SO_RCVBUF 不能小于讀速度, 而且中間還是出現了視窗重疊,是以小于100s 1024 - 98 s 接收最大 5120 KB, 因為出現了強制重新整理寫,時間應該變短,但是還是 98s, 總體不太符合理論, 可能因為 其他一下機制吧。 2048 - 98 s 接收最大 10240 KB 4096 - 63 s 接收最大 20480 KB, 因為出現了強制重新整理寫,時間變短了 8192 - 48 s 接收最大 40960,實際 32768 KB, 因為出現了強制重新整理寫,時間更短了 81920 - 33 s 接收最大 40960, 實際 38912 KB --- 發送端資料不夠; , 因為出現了強制重新整理寫,時間更短了 819200 - 33 s 接收最大 10240 KB, 因為出現了強制重新整理寫,時間沒有更短了,可能因為 超過了 65536 8192000 - 33 s 接收最大 10240KB 可見 ,服務端 花了98s 接收全部資料,基本上是 SO_RCVBUF  越大,強制重新整理寫的間隔越小, 耗時越少。 修改一下, 發送端資料 200 kb, 每次1 kb, 用戶端 不 考慮 writable , isWritable = true 次數139, 服務端實際接收 200 KB, 沒有丢失 8192 - 77 s 接收最大 40960,實際 40960 KB 發送端資料 200 kb, 每次1 kb, 用戶端考慮 writable , isWritable = true 次數 148, 服務端實際接收 148 KB, 沒有丢失 8192 - 48 s 接收最大 40960,實際 40960 KB 發送端資料 2000, 每次10 kb,5ms 一次 10KB, 用戶端考慮 writable , isWritable = true 次數 16, 服務端實際接收 160 KB, 沒有丢失 8192 - 62 s 接收最大 40960,實際 40960 KB 發送端資料 2000, 每次10 kb,50 ms 一次 10KB, 用戶端考慮 writable , isWritable = true 次數 23, 服務端實際接收 230 KB, 沒有丢失 發送端資料 2000, 每次1000 kb,50 ms 一次 100KB, 用戶端考慮 writable , isWritable = true 次數 2, 服務端實際接收 2000 KB, 沒有丢失 8192 - .. s 接收最大 40960,實際 40960 KB

當讀的資料導緻的延遲超出了max(預設15s),那麼就最多等待15s,具體實作在 io.netty.handler.traffic.AbstractTrafficShapingHandler#channelRead,

每隔wait 去重新設定 可讀性。 如果 config.isAutoRead 那麼設定為false, 然後等待wait 時間,然後重置可讀性為true !

第一次的時候 autoRead 是true,是以進了if,把autoRead 設定為 false(然後就接收不到消息了),然後schedule 一個 reopenTask,delay 是1s,然後1s後, reopen即把把autoRead 設定true,然後又接收資料,然後反複這樣。。

其中 ReopenReadTimerTask 做成了一個屬性, 把任務緩存了起來,不用每次建立, 效率起見!

日志分析:

用戶端不考慮阻塞,發送資料 200 kb, 每次1 kb,1s 10次, 即用戶端寫速度是 10KB /s :服務端讀限制 1KB /s 的時候:

用戶端

可見,基本上是 10 KB/s

服務端

可見, 第1秒收到 1038 (其中 14B 可能是 建立連接配接的資料, 也不能忽略), 第2秒是 2048,第4秒是27648, 然後過了 15s 收到 32768 即32 KB  ,, 然後又過了 15s 收到 65536 即64 KB   

服務端限速 是 1 KB/s, 用戶端速度 是 10 KB/s,  但為什麼收到的資料是這樣的節奏? 估計是緩沖區大小 動态調整的結果。

netty 的流量整形讀測試

 第1秒之内,用戶端總共發送10 KB ,服務端因為限速隻接收了其中的第一個 1 KB + 14B,第2個 1 KB則被限流,需要等待1.0137s (可能是異步阻塞),然後把 autoread 設定為false,然後緩沖區大小 調整為2,第3、4個 被放入讀緩沖區,被緩沖; 以後7個1KB 可能無法讀取,故總共9 KB 被延遲;

 第2秒之内,autoread  reopen, 然後用戶端又是發送10 KB ,服務端可能沒有接收其中任何 1 KB,因為緩沖區大小 為2, 故消費了2KB, 然後等待2S,然後又設定了autoread 為true,然後緩沖區被設定為27648  即27KB,..

然後 第3秒接收了0, 因為autoread 為false,無法讀取,屬于等待期間

 第4秒,接收了27 KB, 但是最大等待時間是 15s,然後 緩沖區大小 被調整為32 、 64 KB, 最大就是 64, 讓固定這樣一個速度接收。

用戶端代碼做一些優化: 

即連結成功之後休息一秒,然後才進入循環 正式發送資料:

 此時服務端日志:

基本上是一樣的結果