日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

基于 Netty 如何实现高性能的 HTTP Client 的连接池

發布時間:2025/3/21 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于 Netty 如何实现高性能的 HTTP Client 的连接池 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

使用netty作為http的客戶端,pool又該如何進行設計。本文將會進行詳細的描述。

1. 復用類型的選型

1.1 channel 復用

多個請求可以共用一個channel

模型如下

模型

特點

  • 1:callback隊列為回調隊列。 不同的callback通過一個全局的id進行標識。發送的時候會把該id發到服務端,服務端在回復的時候必須把該id再返回到客戶端。

  • 2:獲取連接只需要隨機獲取一個channel即可,將callback添加到隊列里面。

  • 3: 獲取連接時消除了鎖的競爭,性能高效。

  • 4:結構簡單。

示例

  • osp(唯品會的SOA框架) client pool實現(thrift協議)

  • spray 的 akka client pool

約束

需要服務端配合支持channel復用。需要有一個全局唯一的id用于識別請求。 通常id先發給服務端,服務端還要把id會給客戶端。

1.2 channel 獨享

每個請求獨立使用一個channel。

模型如下

模型

特點

  • 1:同一個channel同時只給一個request使用。

  • 2:連接池的設計較為復雜。

示例

  • 1:數據庫連接池[druid,c3p0,dbcp,hikaricp,caelus(唯品會內部連接池)]

  • 2:netty的http pool ; apache的httpclient pool, httpasyncclient pool ; nginx的pool。

1.3 選擇

由于http1.1協議原生不支持channel復用(http2是支持的),如果需要支持,則需要在header里面加入一個唯一id,所有的應用服務器均需要進行改動。為了和nginx的連接池保持一致,確定使用channel的獨享方式。

?

2. 組件選型

組件優點缺點
common-pool功能完整不支持異步連接
rxnetty pool功能完整,支持netty使用的為rxjava機制
netty poolnetty原生實現功能較為簡單

選擇netty pool作為連接池的實現。4.0.33版本有該功能,可能老版本沒有pool的功能。

?

3. pool的設計

3.1 模型

模型

通過ip,port路由到對應的pool,每個pool之間完全獨立。

3.2 主要功能點

功能點

3.3 獲取連接

  • 1:通過控制最大連接數,來避免無限的創建連接。

  • 2:當超過最大連接數時,則需要等待。由于整個流程是全異步的,需要將當前信息進行任務封裝注冊回調。

  • 3:需要設置等待連接的個數及超時時間,避免把內存給撐爆。

  • 4:需要對獲取的連接進行有效性檢查。一般只需校驗channel.isactive()即可。如果檢驗失敗,需要重新獲取有效連接。

3.4 資源池

  • 1:使用無鎖的ConcurrentLinkedDeque 雙向隊列來存放所有idle的連接(jdk1.7才有該類)。

  • 2:該隊列通過cas的操作來避免同步。 由于拿到連接后業務執行的速度較慢,所以這里的cas沖突應該很小。

3.5 歸還連接

歸還連接

歸還連接主要包含兩部分:正常release和異常的forceClose

  • 1:在netty中,如果收到FIN(服務端發送的正常close請求),則會通知到netty的channelInactive接口,需要在該接口處進行forceClose.

  • 2:收到RST(服務端非正常的關閉),則會通知到exceptionCaught接口,需要在該接口處進行foreclose。關于RST的問題可參考:http://blog.csdn.net/hetaohappy/article/details/51851880

  • 3:在收到正常數據后(channel的channelRead接口),需要判斷header里面是否有Connection:close,如果有,則進行forceClose,否則進行release

  • 4:如果空閑超時,則關閉連接,來避免連接一直被無效的占用。只需要增加IdleStateHandler ,判斷連接空閑超時,則fire一個event事件。只需要注冊對該事件的監聽,進行foreclose即可。

  • 5:占有超時:連接在規定的時間內未還,則進行forceClose。

6:發送請求時,發現channel已經被close掉或者其他io異常,則進行forceClose。

7:forceclose接口里面,需要通過一個狀態位來控制是否操作 acquiredChannelCount(已獲取連接數)。由于調用forceclose,連接可能在資源池中,如果操作該字段,會導致該字段統計不準確。

3.6 超時控制

獲取連接timeout

在規定的時間內沒有獲取到連接,則拋異常。

  • 1:一般實現是通過ReentrantLock來設置lock的超時時間或者直接通過unsafe.park設置超時時間。該種機制會對當前線程進行block。

  • 2:由于netty是純異步機制,如果進行block,會嚴重影響性能。所以這里是將當前信息進行task封裝,然后schedule一個定時任務。如果在設定時間內該task沒有被消費,則會拋出timeout的異常。

建立連接timeout

  • 1:在BIO中,通過設置socket的connect(SocketAddress endpoint, int timeout) 時間即可。Tips:該值不要和setSoTimeout(int timeout)混淆,sotimeout是設置調用read的超時時間。

  • 2:在NIO中,需要業務自己控制連接的超時時間。 一般是通過schedule一個定時任務來控制超時時間。(在netty中即使用的該機制)

連接空閑timeout

  • 1: 通過設置一個handler(IdleStateHandler ),在新建連接的時候schedule一個任務(時間為空閑超時時間),在調用read或者write方法的時候,進行時間的更新。如果任務運行的時候發現空閑超時,則進行event的觸發。

  • 2:業務handler捕獲該event,進行連接空閑超時的處理。

連接被占有timeout

避免連接泄露

  • 1:在獲取連接的時候 schedule一個任務(時間為連接被占用的timeout),在歸還的時候會cancel該任務。如果該任務被運行,說明在規定的時間沒有歸還,則進行timeout的處理。

3.7 性能優化

  • 1:資源池無鎖化:ConcurrentLinkedDeque (前面已有介紹)

  • 2:acquiredChannelCount(已獲取連接數)的無鎖化(該字段用來控制是否達到了最大連接數,正常情況為獲取連接后加1,歸還連接后減1)。

連接池均會通過acquiredChannelCount來控制當前已經獲取的連接個數。該參數會面臨著多線程的競爭,需要進行同步或者cas的設計。如何設計讓acquiredChannelCount完全不用考慮多線程競爭?

看能不能從akka的設計中找點思路: akka消除競爭的方式就是讓一個actor同一時刻只能在一個線程中運行,這樣actor里面所有的全局參數就不需要考慮多線程競爭,一個actor里面所有的任務都是串行執行的,完全消除競爭。

那么能不能串行操作acquiredChannelCount呢? 答案是可以的,并且在netty中實現非常簡單,只需要實現如下代碼即可:

if?(executor.inEventLoop())?{acquiredChannelCount++;}?else{executor.execute(newOneTimeTask()?{@Overridepublic?void?run()?{acquiredChannelCount++;}} }

其中executor 就是一個固定的線程。判定當前執行的線程是否是executor這個線程,如果是則直接執行。如果不是,則放到executor線程的隊列里達到串行操作的目的(類似于actor的mailbox) (netty的設計及抽象能力確實非常高)

3.8 配置參數

  • http_pool_aquire_timeout?:獲取連接超時時間:默認為5000ms

  • http_pool_maxConnections:連接大小:默認為1000

  • http_connection_timeout?:建立連接的超時時間:默認為5000ms

  • http_pool_idle_timeout:連接空閑多久關閉:默認為:30分鐘

  • http_pool_maxPending:連接池不夠用,最大允許有多少個pendingRequest:默認為無限大

  • http_pool_maxHolding:拿連接的使用時間。在規定時間未還,則強制close掉。默認為5000ms。

?

4. 面臨的問題

  • 1:所有的操作都是純異步,導致callback嵌套的特別深(netty通過promise機制,來方便callback的使用),如果控制不好,很容易出問題。

  • 2:連接被require后,一定要保證歸還,由于異步特性,很容易在某些異常下將連接漏還(筆者遇到在高并發下由于代碼bug導致漏還的情況)

  • 3:如何避免在拿到連接后,同時web服務器(http的keepalive機制)將該連接給close掉了,導致執行的失敗。有兩種解決方案可以參考。

    • 捕獲執行失敗的異常,如果是特定的異常,則forceClose當前的連接,重新拿一個連接進行訪問。如果超過重試次數,則拋出異常。

    • 如何確定該線程定時的時間。后端web服務器對連接的超時時間可能不一致,該定時時間一定要小于web服務器的連接超時時間。

    • 心跳執行的接口問題。需要所有的web服務器均需要實現固定的接口進行心跳檢測,可行性比較差。

    • 3.1:可以參考common-pool的設計思想,在后端開啟一個線程定時對所有連接進行心跳檢測。問題:

      • 如何確定該線程定時的時間。后端web服務器對連接的超時時間可能不一致,該定時時間一定要小于web服務器的連接超時時間。

      • 心跳執行的接口問題。需要所有的web服務器均需要實現固定的接口進行心跳檢測,可行性比較差。

    • 3.2:重試機制:

      • 捕獲執行失敗的異常,如果是特定的異常,則forceClose當前的連接,重新拿一個連接進行訪問。如果超過重試次數,則拋出異常。

總結

以上是生活随笔為你收集整理的基于 Netty 如何实现高性能的 HTTP Client 的连接池的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。