作者 | 顏高飛
來源 | 阿里巴巴雲原生公眾號
Dubbo 是一款輕量級的開源 Java 服務框架,是眾多企業在建設分布式服務架構時的首選。中國工商銀行自 2014 年開始探索分布式架構轉型工作,基於開源 Dubbo 自主研發了分布式服務平台。
Dubbo 框架在提供方消費方數量較小的服務規模下,運行穩定、性能良好。隨着銀行業務線上化、多樣化、智能化的需求越來越旺盛,在可預見的未來,會出現一個提供方為數千個、甚至上萬個消費方提供服務的場景。
在如此高負載量下,若服務端程序設計不夠良好,網絡服務在處理數以萬計的客戶端連接時、可能會出現效率低下甚至完全癱瘓的情況,即為 C10K 問題。那么,基於 Dubbo 的分布式服務平台能否應對復雜的 C10K 場景?為此,我們搭建了大規模連接環境、模擬服務調用進行了一系列探索和驗證。
C10K 場景下 Dubbo 服務調用出現大量交易失敗
1. 准備環境
使用 Dubbo2.5.9(默認 netty 版本為 3.2.5.Final)版本編寫服務提供方和對應的服務消費方。提供方服務方法中無實際業務邏輯、僅 sleep 100ms;消費方側配置服務超時時間為 5s,每個消費方啟動后每分鍾調用1次服務。
准備 1 台 8C16G 服務器以容器化方式部署一個服務提供方,准備數百台 8C16G 服務器以容器化方式部署 7000 個服務消費方。
啟動 Dubbo 監控中心,以監控服務調用情況。
2. 定制驗證場景,觀察驗證結果
驗證情況不盡如人意。C10K 場景下 Dubbo 服務調用存在超時失敗的情況。
如果分布式服務調用耗時長,從服務消費方到服務提供方全鏈路節點都會長時間占用線程池資源,增加了額外的性能損耗。而當服務調用並發突增時,很容易造成全鏈路節點堵塞,從而影響其他服務的調用,並進一步造成整個服務集群性能下降甚至整體不可用,導致發生雪崩。服務調用超時問題不可忽視。因此,針對該 C10K 場景下 Dubbo 服務調用超時失敗情況我們進行了詳細分析。
C10K 場景問題分析
根據服務調用交易鏈路,我們首先懷疑交易超時是因為提供方或消費方自身進程卡頓或網絡存在延遲導致的。
因此,我們在存在交易失敗的提供方、消費方服務器上開啟進程 gc 日志,多次打印進程 jstack,並在宿主機進行網絡抓包。
1. 觀察 gc 日志、jstack
提供方、消費方進程 gc 時長、gc 間隔、內存使用情況、線程堆棧等無明顯異常,暫時排除 gc 觸發 stop the world 導致超時、或線程設計不當導致阻塞而超時等猜想。
2. 針對兩種場景下的失敗交易進行觀察
針對以上兩種場景下的失敗交易,分別觀察網絡抓包,對應有以下兩種不同的現象:
針對場景 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檢測與提供方連接,若連接異常,發起重連,連接恢復。此后交易正常。
3. C10K 場景問題分析總結
總結以上造成交易超時的原因有兩個:
- 心跳機制導致 netty worker 線程忙碌。在每個心跳任務中,提供方向所有 1 個心跳周期內未收發過報文的消費方發送心跳;消費方向所有 1 個心跳周期內未收發過報文的提供方發送心跳。提供方上所連接的消費方較多,導致心跳報文堆積;同時,處理心跳過程消耗較多 CPU,影響了業務報文的處理時效。
- 全連接隊列容量不足。在提供方重啟后該隊列溢出,導致大量單邊連接產生。單邊連接下首筆交易大概率超時失敗。
4. 下一步思考
- 針對以上場景 1:如何能降低單個 netty worker 線程處理心跳的時間,加速 IO 線程的運行效率?初步設想了如下幾種方案:
- 降低單個心跳的處理耗時
- 增加 netty worker 線程數,降低單個 IO 線程的負載
- 打散心跳,避免密集處理
- 針對以上場景 2:如何規避首筆大量半連接導致的交易失敗?設想了如下方案:
- 增加 TCP 全連接隊列的長度,涉及操作系統、容器、Netty
- 提高服務端 accept 連接的速度
交易報文處理效率提升
1. 逐層優化
基於以上設想,我們從系統層面、Dubbo 框架層面進行了大量的優化,以提升 C10K 場景下交易處理效率,提升服務調用的性能容量。
優化內容包括以下方面:
具體涉及優化的框架層如下:
經對各優化內容逐項驗證,各措施均有不同程度的提升,效果分別如下:
2. 綜合優化驗證效果
綜合運用以上優化效果最佳。在此 1 個提供方連接 7000 個消費方的驗證場景下,重啟提供方后、長時間運行無交易超時場景。對比優化前后,提供方 CPU 峰值下降 30%,消費方與提供方之間處理時差控制在 1ms 以內,P99 交易耗時從 191ms 下降至 125ms。在提升交易成功率的同時,有效減少了消費方等待時間、降低了服務運行資源占用、提升了系統穩定性。
3. 線上實際運行效果
基於以上驗證結果,中國工商銀行在分布式服務平台中集成了以上優化內容。截至發文日期,線上已存在應用一個提供方上連接上萬個消費方的場景。落地該優化版本后,在提供方版本升級、及長時間運行下均無異常交易超時情況,實際運行效果符合預期。
未來展望
中國工商銀行深度參與 Dubbo 社區建設,在 Dubbo 金融級規模化運用的過程中遇到了諸多技術挑戰,為滿足金融級高敏交易的苛刻運行要求,開展了大規模自主研發,並通過對 Dubbo 框架的擴展和定制持續提升服務體系的穩定性,以“源於開源、回饋開源”的理念將通用增強能力不斷貢獻至開源社區。
未來,我們將持續致力於 Dubbo 的金融級規模化應用,協同社區繼續提升 Dubbo 的性能容量和高可用水平,加速金融行業數字化創新和轉型及基礎核心關鍵的全面自主可控。
作者簡介
顏高飛,微服務領域架構師,主要從事服務發現、高性能網絡通信等研發工作,擅長 ZooKeeper、Dubbo、RPC 協議等技術方向。
在 PC 端登錄 start.aliyun.com 知行動手實驗室,沉浸式體驗在線交互教程。