簡介: Dubbo 是一款輕量級的開源 Java 服務框架,是衆多企業在建設分佈式服務架構時的首選。中國工商銀行自 2014 年開始探索分佈式架構轉型工做,基於開源 Dubbo 自主研發了分佈式服務平臺。服務器
做者 | 顏高飛
來源 | 阿里巴巴雲原生公衆號網絡
Dubbo 是一款輕量級的開源 Java 服務框架,是衆多企業在建設分佈式服務架構時的首選。中國工商銀行自 2014 年開始探索分佈式架構轉型工做,基於開源 Dubbo 自主研發了分佈式服務平臺。架構
Dubbo 框架在提供方消費方數量較小的服務規模下,運行穩定、性能良好。隨着銀行業務線上化、多樣化、智能化的需求愈來愈旺盛,在可預見的將來,會出現一個提供方爲數千個、甚至上萬個消費方提供服務的場景。併發
在如此高負載量下,若服務端程序設計不夠良好,網絡服務在處理數以萬計的客戶端鏈接時、可能會出現效率低下甚至徹底癱瘓的狀況,即爲 C10K 問題。那麼,基於 Dubbo 的分佈式服務平臺可否應對複雜的 C10K 場景?爲此,咱們搭建了大規模鏈接環境、模擬服務調用進行了一系列探索和驗證。框架
使用 Dubbo2.5.9(默認 netty 版本爲 3.2.5.Final)版本編寫服務提供方和對應的服務消費方。提供方服務方法中無實際業務邏輯、僅 sleep 100ms;消費方側配置服務超時時間爲 5s,每一個消費方啓動後每分鐘調用1次服務。分佈式
準備 1 臺 8C16G 服務器以容器化方式部署一個服務提供方,準備數百臺 8C16G 服務器以容器化方式部署 7000 個服務消費方。微服務
啓動 Dubbo 監控中心,以監控服務調用狀況。工具
驗證狀況不盡如人意。C10K 場景下 Dubbo 服務調用存在超時失敗的狀況。性能
若是分佈式服務調用耗時長,從服務消費方到服務提供方全鏈路節點都會長時間佔用線程池資源,增長了額外的性能損耗。而當服務調用併發突增時,很容易形成全鏈路節點堵塞,從而影響其餘服務的調用,並進一步形成整個服務集羣性能降低甚至總體不可用,致使發生雪崩。服務調用超時問題不可忽視。所以,針對該 C10K 場景下 Dubbo 服務調用超時失敗狀況咱們進行了詳細分析。優化
根據服務調用交易鏈路,咱們首先懷疑交易超時是由於提供方或消費方自身進程卡頓或網絡存在延遲致使的。
所以,咱們在存在交易失敗的提供方、消費方服務器上開啓進程 gc 日誌,屢次打印進程 jstack,並在宿主機進行網絡抓包。
提供方、消費方進程 gc 時長、gc 間隔、內存使用狀況、線程堆棧等無明顯異常,暫時排除 gc 觸發 stop the world 致使超時、或線程設計不當致使阻塞而超時等猜測。
針對以上兩種場景下的失敗交易,分別觀察網絡抓包,對應有如下兩種不一樣的現象:
針對場景 1:提供方穩定運行過程當中交易超時
跟蹤網絡抓包及提供方、消費方交易日誌。消費方發起服務調用請求發起後,在提供方端迅速抓到消費方請求報文,但提供方從收到請求報文到開始處理交易耗時 2s+。
同時,觀察交易請求響應的數據流。提供方業務方法處理完畢後到向消費方發送回包之間也耗時 2s+,此後消費方端迅速收到交易返回報文。但此時交易總耗時已超過 5s、超過服務調用超時時間,致使拋出超時異常。
由此,判斷致使交易超時的緣由不在消費方側,而在提供方側。
針對場景 2:提供方重啓後大量交易超時
服務調用請求發起後,提供方迅速收到消費方的請求報文,但提供方未正常將交易報文遞交給應用層,而是回覆了 RST 報文,該筆交易超時失敗。
觀察在提供方重啓後 1-2 分鐘內出現大量的 RST 報文。經過部署腳本,在提供方重啓後每隔 10ms 打印 established 狀態的鏈接數,發現提供方重啓後鏈接數未能迅速恢復到 7000,而是通過 1-2 分鐘後鏈接數才恢復至正常數值。而在此過程當中,逐臺消費方上查詢與提供方的鏈接狀態,均爲 established,懷疑提供方存在單邊鏈接狀況。
咱們繼續分別分析這兩種異常場景。
場景 1:提供方實際交易先後均耗時長、致使交易超時
細化收集提供方的運行狀態及性能指標:
部署服務器系統性能採集工具 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檢測與提供方鏈接,若鏈接異常,發起重連,鏈接恢復。此後交易正常。
總結以上形成交易超時的緣由有兩個:
針對以上場景 1:如何能下降單個 netty worker 線程處理心跳的時間,加速 IO 線程的運行效率?初步設想了以下幾種方案:
針對以上場景 2:如何規避首筆大量半鏈接致使的交易失敗?設想了以下方案:
基於以上設想,咱們從系統層面、Dubbo 框架層面進行了大量的優化,以提高 C10K 場景下交易處理效率,提高服務調用的性能容量。
優化內容包括如下方面:
具體涉及優化的框架層以下:
經對各優化內容逐項驗證,各措施均有不一樣程度的提高,效果分別以下:
綜合運用以上優化效果最佳。在此 1 個提供方鏈接 7000 個消費方的驗證場景下,重啓提供方後、長時間運行無交易超時場景。對比優化先後,提供方 CPU 峯值降低 30%,消費方與提供方之間處理時差控制在 1ms 之內,P99 交易耗時從 191ms 降低至 125ms。在提高交易成功率的同時,有效減小了消費方等待時間、下降了服務運行資源佔用、提高了系統穩定性。
基於以上驗證結果,中國工商銀行在分佈式服務平臺中集成了以上優化內容。截至發文日期,線上已存在應用一個提供方上鍊接上萬個消費方的場景。落地該優化版本後,在提供方版本升級、及長時間運行下均無異常交易超時狀況,實際運行效果符合預期。
中國工商銀行深度參與 Dubbo 社區建設,在 Dubbo 金融級規模化運用的過程當中遇到了諸多技術挑戰,爲知足金融級高敏交易的苛刻運行要求,開展了大規模自主研發,並經過對 Dubbo 框架的擴展和定製持續提高服務體系的穩定性,以「源於開源、回饋開源」的理念將通用加強能力不斷貢獻至開源社區。
將來,咱們將持續致力於 Dubbo 的金融級規模化應用,協同社區繼續提高 Dubbo 的性能容量和高可用水平,加速金融行業數字化創新和轉型及基礎核心關鍵的全面自主可控。
做者簡介
顏高飛,微服務領域架構師,主要從事服務發現、高性能網絡通訊等研發工做,擅長 ZooKeeper、Dubbo、RPC 協議等技術方向。
原文連接本文爲阿里雲原創內容,未經容許不得轉載。