簡介: 未來,中國工商銀行将持續緻力于 Dubbo 的金融級規模化應用。
作者:顔高飛,微服務領域架構師,主要從事服務發現、高性能網絡通信等研發工作,擅長 ZooKeeper、Dubbo、RPC 協定等技術方向。
Dubbo是一款輕量級的開源Java服務架構,是衆多企業在建設分布式服務架構時的首選。中國工商銀行自2014年開始探索分布式架構轉型工作,基于開源Dubbo自主研發建設了分布式服務平台。Dubbo架構在提供方消費方數量較小的服務規模下,運作穩定、性能良好。
随着銀行業務線上化、多樣化、智能化的需求越來越旺盛,在可預見的未來,會出現一個提供方為數千個、甚至上萬個消費方提供服務的場景。在如此高負載量下,若服務端程式設計不夠良好,網絡服務在處理數以萬計的用戶端連接配接時、可能會出現效率低下甚至完全癱瘓的情況,即為C10K問題。那麼,基于dubbo的分布式服務平台能否應對複雜的C10K場景?為此,我們搭建了大規模連接配接環境、模拟服務調用進行了一系列探索和驗證。
C10K場景下Dubbo服務調用出現大量交易失敗
準備環境:
使用dubbo2.5.9(預設netty版本為3.2.5.Final)版本編寫服務提供方和對應的服務消費方。提供方服務方法中無實際業務邏輯、僅sleep 100ms;消費方側配置服務逾時時間為5s,每個消費方啟動後每分鐘調用1次服務。
準備1台8C16G伺服器以容器化方式部署一個服務提供方,準備數百台8C16G伺服器以容器化方式部署7000個服務消費方。
啟動dubbo監控中心,以監控服務調用情況。
定制驗證場景,觀察驗證結果:
操作步驟 | 觀察内容 | 驗證結果 | |
場景1 | 先啟動服務提供方,後分批啟動消費方 | 調用1小時觀察交易情況 | 存在零星交易逾時失敗。消費方分散在多台伺服器上。 |
場景2 | 在服務正常調用一段時間後,重新開機提供方 | 觀察提供方重新開機後的表現 | 在提供方重新開機後1-2分鐘記憶體在大量交易逾時失敗,後逐漸恢複。消費方分散在多台伺服器上。 |
驗證情況不盡如人意,C10K場景下dubbo服務調用存在逾時失敗的情況。
如果分布式服務調用耗時長,從服務消費方到服務提供方全鍊路節點都會長時間占用線程池資源,增加了額外的性能損耗。而當服務調用并發突增時,很容易造成全鍊路節點堵塞,進而影響其他服務的調用,并進一步造成整個服務叢集性能下降甚至整體不可用,導緻發生雪崩。服務調用逾時問題不可忽視。是以,針對該C10K場景下dubbo服務調用逾時失敗情況我們進行了詳細分析。
C10K場景問題分析
根據服務調用交易鍊路,我們首先懷疑交易逾時是因為提供方或消費方自身程序卡頓或網絡存在延遲導緻的。

是以,我們在存在交易失敗的提供方、消費方伺服器上開啟程序gc日志,多次列印程序jstack,并在主控端進行網絡抓包。
觀察gc日志、jstack
提供方、消費方程序gc時長、gc間隔、記憶體使用情況、線程堆棧等無明顯異常,暫時排除gc觸發stop the world導緻逾時、或線程設計不當導緻阻塞而逾時等猜想。
針對以上兩種場景下的失敗交易,分别觀察網絡抓包,對應有以下兩種不同的現象:
針對場景1:提供方穩定運作過程中交易逾時。
跟蹤網絡抓包及提供方、消費方交易日志。消費方發起服務調用請求發起後,在提供方端迅速抓到消費方請求封包,但提供方從收到請求封包到開始處理交易耗時2s+。
同時,觀察交易請求響應的資料流。提供方業務方法處理完畢後到向消費方發送回包之間也耗時2s+,此後消費方端迅速收到交易傳回封包。但此時交易總耗時已超過5s、超過服務調用逾時時間,導緻抛出逾時異常。
由此,判斷導緻交易逾時的原因不在消費方側,而在提供方側。
針對場景2:提供方重新開機後大量交易逾時。
服務調用請求發起後,提供方迅速收到消費方的請求封包,但提供方未正常将交易封包遞交給應用層,而是回複了RST封包,該筆交易逾時失敗。
觀察在提供方重新開機後1-2分鐘内出現大量的RST封包。通過部署腳本,在提供方重新開機後每隔10ms列印established狀态的連接配接數,發現提供方重新開機後連接配接數未能迅速恢複到7000,而是經過1-2分鐘後連接配接數才恢複至正常數值。而在此過程中,逐台消費方上查詢與提供方的連接配接狀态,均為established,懷疑提供方存在單邊連接配接情況。
我們繼續分别分析這兩種異常場景。
場景1:提供方實際交易前後均耗時長、導緻交易逾時
細化收集提供方的運作狀态及性能名額:
- 在提供方伺服器上每隔3s收集服務提供方jstack,觀察到netty worker線程每60s左右頻繁處理心跳。
- 同時列印top -H,觀察到占用cpu時間片較多的線程排名前10中包含9個netty worker線程。因提供方伺服器為8C,dubbo預設netty worker線程數為9個,即所有9個netty worker線程均較忙碌。
- 部署伺服器系統性能采集工具nmon,觀察到cpu每隔60秒左右産生毛刺;相同時間網絡封包數也有毛刺。
- 部署ss -ntp連續列印網絡接收隊列、發送隊列中的資料積壓情況。觀察到在耗時長的交易時間點附近隊列堆積較多。
- Dubbo服務架構中提供方和消費方發送心跳封包(封包長度為17)的周期為60s,與以上間隔接近。結合網絡抓包,耗時長的交易時間點附近心跳包較多。
根據Dubbo架構的心跳機制,當消費方數量較大時,提供方發送心跳封包、需應答的消費方心跳封包将會很密集。是以,懷疑是心跳密集導緻netty線程忙碌,進而影響交易請求的處理,繼而導緻交易耗時增加。
進一步分析netty worker線程的運作機制,記錄每個netty worker線程在處理連接配接請求、處理寫隊列、處理selectKeys這三個關鍵環節的處理耗時。觀察到每間隔60s左右(與心跳間隔一緻)處理讀取資料包較多、耗時較大,期間存在交易耗時增加的情況。同一時間觀察網絡抓包,提供方收到較多的心跳封包。
是以,确認以上懷疑。心跳密集導緻netty worker線程忙碌,進而導緻交易耗時增長。
場景2:單邊連接配接導緻交易逾時
分析單邊連接配接産生的原因
TCP建立連接配接三次握手的過程中,若全連接配接隊列滿,将導緻單邊連接配接。
全連接配接隊列大小由系統參數net.core.somaxconn及listen(somaxconn,backlog)的backlog取最小值決定。somaxconn是Linux核心的參數,預設值是128;backlog在建立Socket時設定,dubbo2.5.9中預設backlog值是50。是以,生産環境全連接配接隊列是50。通過ss指令(Socket Statistics)也查得全連接配接隊列大小為50。
觀察TCP連接配接隊列情況,證明存在全連接配接隊列溢出的現象。
即:全連接配接隊列容量不足導緻大量單邊連接配接産生。因在本驗證場景下,訂閱提供方的消費方數量過多,當提供方重新開機後,注冊中心向消費方推送提供方上線通知,所有消費方幾乎同時與提供方重建連接配接,導緻全連接配接隊列溢出。
分析單邊連接配接影響範圍
單邊連接配接影響範圍多為消費方首筆交易,偶發為首筆開始連續失敗2-3筆。
建立為單邊的連接配接下,交易非必然失敗。三次握手全連接配接隊列滿後,若半連接配接隊列空閑,提供方建立定時器向消費方重傳syn+ack,重傳預設5次,重傳間隔以倍數增長,1s..2s..4s..共31s。在重傳次數内,若全連接配接隊列恢複空閑,消費方應答ack、連接配接建立成功。此時交易成功。
在重傳次數内,若全連接配接隊列仍然忙碌,新交易到達逾時時間後失敗。
到達重傳次數後,連接配接被丢棄。此後消費方發送請求,提供方應答RST。後交易到達逾時時間失敗。
根據Dubbo的服務調用模型,提供方發送RST後,消費方抛出異常Connection reset by peer,後斷開與提供方的連接配接。而消費方無法收到目前交易的響應封包、導緻逾時異常。同時,消費方定時器每2s檢測與提供方連接配接,若連接配接異常,發起重連,連接配接恢複。此後交易正常。
C10K場景問題分析總結
總結以上造成交易逾時的原因有兩個:
- 心跳機制導緻netty worker線程忙碌。在每個心跳任務中,提供方向所有1個心跳周期内未收發過封包的消費方發送心跳;消費方向所有1個心跳周期内未收發過封包的提供方發送心跳。提供方上所連接配接的消費方較多,導緻心跳封包堆積;同時,處理心跳過程消耗較多CPU,影響了業務封包的處理時效。
- 全連接配接隊列容量不足。在提供方重新開機後該隊列溢出,導緻大量單邊連接配接産生。單邊連接配接下首筆交易大機率逾時失敗。
下一步思考
針對以上場景1:如何能降低單個netty worker線程處理心跳的時間,加速IO線程的運作效率?初步設想了如下幾種方案:
- 降低單個心跳的處理耗時
- 增加netty worker線程數,降低單個IO線程的負載
- 打散心跳,避免密集處理
針對以上場景2:如何規避首筆大量半連接配接導緻的交易失敗?設想了如下方案:
- 增加TCP全連接配接隊列的長度,涉及作業系統、容器、Netty
- 提高服務端accept連接配接的速度
交易封包處理效率提升
逐層優化
基于以上設想,我們從系統層面、dubbo架構層面進行了大量的優化,以提升C10K場景下交易處理效率,提升服務調用的性能容量。
優化内容包括以下方面:
具體涉及優化的架構層如下:
經對各優化内容逐項驗證,各措施均有不同程度的提升,效果分别如下:
優化内容 | 優化效果 |
tcp全連接配接隊列擴容 | 提供方重新開機後交易逾時失敗現象消除 |
epoll模型調整 | 提供方重新開機後全連接配接隊列溢出次數明顯降低,連接配接accept速度有所提升 |
心跳繞過序列化 | 提供方在心跳周期無CPU毛刺,CPU峰值降低20% 消費方與提供方之間平均處理時差由27ms降低至3ms 前99%的交易耗時從191ms下降至133ms |
增加Iothreads線程數 | 将預設的iothreads線程數9調整為20後,消費方與提供方之間平均處理時差由27ms降低至14ms 前99%的交易耗時從191ms下降至186ms |
提供方心跳打散 | 從提供方網絡抓包分析,心跳資料包的毛刺峰值從1.5萬/秒壓降至3000/秒 |
消費方心跳打散 | 從提供方網絡抓包分析,心跳資料包幾乎不再有毛刺峰 |
綜合優化驗證效果
綜合運用以上優化效果最佳。在此1個提供方連接配接7000個消費方的驗證場景下,重新開機提供方後、長時間運作無交易逾時場景。對比優化前後,提供方CPU峰值下降30%,消費方與提供方之間處理時差控制在1ms以内,P99交易耗時從191ms下降至125ms。在提升交易成功率的同時,有效減少了消費方等待時間、降低了服務運作資源占用、提升了系統穩定性。
線上實際運作效果
基于以上驗證結果,中國工商銀行在分布式服務平台中內建了以上優化内容。截至發文日期,線上已存在應用一個提供方上連接配接上萬個消費方的場景。落地該優化版本後,在提供方版本更新、及長時間運作下均無異常交易逾時情況,實際運作效果符合預期。
未來展望
中國工商銀行深度參與Dubbo社群建設,在Dubbo金融級規模化運用的過程中遇到了諸多技術挑戰,為滿足金融級高敏交易的苛刻運作要求,開展了大規模自主研發,并通過對Dubbo架構的擴充和定制持續提升服務體系的穩定性,以“源于開源、回饋開源”的理念将通用增強能力不斷貢獻至開源社群。未來,我們将持續緻力于Dubbo的金融級規模化應用,協同社群繼續提升Dubbo的性能容量和高可用水準,加速金融行業數字化創新和轉型及基礎核心關鍵的全面自主可控。
原文連結
本文為阿裡雲原創内容,未經允許不得轉載。