個人博客地址 studyidea.cn,點擊查看更多原創文章
0x00. 翻車現場
那是個月黑風高的夜晚,小黑哥成功將新版本發布到了生產,小心翼翼檢查了應用日志,后續測試小姐姐驗收成功。
恩,小黑哥我還是一如既往的穩~
接着小黑哥就跑到樓下食堂吃個夜宵,誰知正吃到一半,線上運維同學發來幾條告警信息,服務器連接數過多警告,連接數已經飆升到上萬。
天啦擼,趕緊放下正在啃的雞腿,火速跑到工位上查看問題。
0x01. 歷盡艱辛,深入排查
打開電腦,首先確認生產交易一切還正常。查看這段時間日志,發現並沒有什么異常情況,日志都是正常輸出。沒辦法只好再次走查此次改動的代碼,發現全是業務代碼,並沒有任何與網絡連接有關的代碼改動。
問題真的請奇怪,一時半會想不到解決方案,只好先實施重啟大法。重啟過后,連接數下降了,到達了正常閾值。但是不一會連接數持續升高,不一會還是升到上萬。
這下重啟解決不了辦法,只好從應用出發,找找到底什么問題。
這個應用是一個路由服務,會根據上游系統指定路由編碼,將交易分發到下游子系統。架構圖如下:
之前在這篇文章路由系統演化史講過,路由系統使用 Dubbo API ,代碼如下:
由於我們還有另外一套系統,也部署這個應用,但是該系統生產機器連接數卻很少。交叉比對了兩套系統應用的系統配置值,只有 connections 設置不一樣,當前有問題的系統設置為 1000,另外一個系統為 10 。
大致找到原因,也將 connections 設置為 10,重啟應用,生產機器連接數恢復正常。
0x02. 抽絲剝繭,還原經過
首先我們來看下 connections 這個配置的作用,可以直接查看官方文檔http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-reference.html。
下面配置來源於:dubbo:reference
總共可以在三個地方配置 connections 參數,分別為:dubbo:reference,dubbo:consumer,dubbo:provider。
注意:圖中標示地方實際上與源碼存在出入。截止 Dubbo 2.7.3 版本,圖中 ① 處,dubbo:consumer 文檔上顯示為 100,實際源碼默認配置為 0,這點需要注意。另外 ② 處文字描述存在問題,目前 connections 參數主要對 dubbo 協議有用,http 短連接協議還未使用該配置
其中 reference.connections 為服務級別的配置,若未配置將會使用 consumer.connections 配置值。另外這個參數若在 provider.connections 配置,其對服務提供者無效,參數將通過注冊中心傳遞給消費者成為其默認配置。三者實際作用順序如下:
Debug 源碼,connections 最終會在 DubboProtocol#getClients 被使用,方法源碼如下:
Dubbo 協議默認將會使用 **Netty **與服務提供者建立長連接
首先將會獲取 connections 配置,規則如上圖,若其大於 0,建立 connections 數量的長連接。
如果一個提供者對外暴露 10 個接口,且其有兩個節點。消費者端引入提供者所有服務,配置 connections=1000。當消費者啟動之后,將會立刻創建 1000x2x10=20000 連接。這就是生產機器連接數飆升的根本原因。
路由服務使用 Dubbo API 編程,服務啟動成功之后,只有上游系統調用路由服務時, Dubbo 才會與與下游服務提供者建立連接,所以現象看起來服務連接數是慢慢激增。
如果未設置 connections 參數,Dubbo 將會創建共享連接(shareconnections)。消費者調用的服務若為同一個服務提供者(IP+PORT 區分),這些服務接口將會共享這些連接。
shareconnections 可以在 dubbo:consumer 配置中配置,也可以在啟動 JVM 參數加入如下配置:
-Dshareconnections=10
如果消費者需要調用同個服務提供者應用的 10 個服務接口,服務提供者提供兩個節點,shareconnections=1000,消費者服務啟動之后,僅會創建 1000*2=2000 連接。
這么對比,shareconnections 與 connections 建立連不是一個量級。
2.1 使用連接
消費者調用服務時,將會隨機從連接數組中取一個連接使用,代碼位於 DubboInvoker#doInvoke
。
2.2 如何正確配置連接數
首先我們來看下單一長連接性能,文檔地址:http://dubbo.apache.org/zh-cn/docs/user/references/protocol/dubbo.html
對於只有少數消費者場景,我們可以使用默認配置,即不配置 connections 參數 。若調用同一個提供者服務過多,可以考慮適當多配增加 shareconnections。最后若某一服務接口調用量特別大,可以考慮為這個服務單獨配置 connections。
0x03. 舉一反三,聊聊其他配置
Dubbo 還有很多配置項,下面着重介紹一些配置參數。
3.1 dubbo.provider.executes
該參數用來控制每個方法最大並行數。如果該值設置為 10 ,每個服務方法若已有 10 個請求正在處理,第 11 個服務請求將會拋出異常,直到之前服務調用完成,正在請求數量小於 10 未知。
一旦設置 executes>0,Dubbo 將會通過 SPI 機制啟用 ExecuteLimitFilter
,源碼還是比較簡單。
3.2 dubbo.reference.actives
這個參數將會控制消費者每個服務每個方法最大並發數。可以通過 dubbo:method.actives 單獨為服務方法設置。如果該值為 10,一旦某個服務某個方法並發數超過 10,第 11 個服務將會等待,若在超時時間內其他請求執行結束,計數值減值小於閾值,第 11 個請求將會被執行,否者將會拋錯。
dubbo.provider
上也可以配置這個值,其將會與 connections 一樣,將會傳遞給消費者。
原理等同上面方法,將會啟用 ActiveLimitFilter
,源碼如下 :
這里需要注意 actives 引起超時與服務端超時區別。
3.3 dubbo.protocol.accepts
服務提供者最大連接數,如果設置 accepts=10,一旦服務提供者連接數大於 10,其余新增連接將會被拒絕。
方法源碼如下:
服務提供者斷開連接,消費端將會打印連接斷開日志。另外消費者會定時檢查長連接可用性,若不可用,將會重新發起連接。所以在消費者端就會看到連接斷開,重連,然后又被服務提供者斷開的現象。
0x04. 總結
本文通過一次生產連接數過多的現象,詳細剖析定位問題的原因。作為一個合格的開發,對於開源框架,我們不僅要會熟練使用,也要了解其底層實現,相關參數設置。一旦參數設置不合理就可能引發生產事故。
另外對於生產系統,監控系統非常重要。比如上面的問題,如果沒有監控發現,小黑哥可能一時半會都不知道有這個問題存在,畢竟平時也不會太關注連接數這個指標。
好快,已經在家呆了兩周了。哎,出不去,又進不來。以后回想,2020 真是一個令人難忘的一年。。。好了,10 號要正視開工了。
歡迎關注我的公眾號:程序通事,獲得日常干貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn