dubbo做爲一款分佈式服務框架,除了提供遠程調用的細節封裝,還提供了基本的服務治理功能,可以粗略地監控系統性能。linux
上圖展現的是dubbo執行流程的原理圖,在客戶端和服務端都有一個程序去統計調用信息,其中有價值的信息有延遲時間、併發數、調用次數等,完成記錄以後,客戶端和服務端分別會定時發送到監控平臺,監控平臺彙總以後,算出平均qps(每秒調用次數)和平均rt(每次調用延時)等數據並展示在監控平臺頁面上git
最近公司用上了dubbo做爲dsp引擎和算法平臺之間的一個分佈式服務框架,目的是解開引擎和算法之間的強耦合。github
根據dubbo監控平臺觀察到的現象(紅線爲客戶端數據,藍線爲服務端數據)算法
根據dubbo監控平臺現象得知,qps較低時,客戶端rt並不高,可是qps較大時,客戶端rt很是高,而且持續穩定在10ms左右。這對於性能要求很高的廣告引擎是沒法容忍的。緩存
###線上環境性能優化
14臺客戶端,5臺服務端,配置信息:24核cpu+64g內存+1000MB/s帶寬網絡
延遲較高,就得先查看資源使用有沒有出現瓶頸的地方。根據監控信息顯示,服務端延遲基本爲0,客戶端延遲很高。因此頗有多是客戶端出現了瓶頸,因此重點先放到客戶端這一邊,先肯定一臺客戶端的機器(10.100.51.175)來分析問題。併發
因爲目前rpc調用中使用到的資源主要有cpu、內存、網絡,因爲不存在磁盤I/O,因此排除掉這個資源的影響。oracle
性能有問題,首先派上用場的工具就是top。top能夠粗略檢測cpu使用狀況。框架
因而可知總cpu使用率並不高。接着用vmstat查看cpu上等待和正在運行的任務
看到等待的任務並很少,不過這裏能夠看到cs上下文切換較高,有多是線程較多致使不少競爭。這個可能會影響到性能,先記錄下來。
上面提到的都是對cpu總體排查的方式,接着也不要漏過對每一個cpu進行排查的方式。使用mpstat檢查每個cpu
看到沒有一個cpu處於繁忙狀態
根據上述top工具截圖,能夠發現64g內存絕大部分都被分配給了應用程序,可是經過進程信息區能夠查看到每一個進行對內存使用率並不高。
根據上述vmstat工具截圖,能夠觀察到si(換入的內存)和so(換出的內存)都是0,因此係統不會由於內存換頁而產生性能問題。
先使用ethtool來檢測一下網卡eth0的帶寬
發現確實是千兆網卡。那麼在千兆網卡上面,咱們程序的利用率能達到多少呢?可使用一個叫nicstat的工具來統計利用率
從上圖能夠發現,咱們的網絡利用率很低(%Util),也沒有出現丟包(Drops)的狀況,說明咱們數據傳輸速度較慢,這個多是問題所在,暫時先記錄下來。
從總體上只能大概有個方向,具體排查還得從程序自己着手。這時就得根據線上環境,模擬一個測試平臺,在測試平臺來定位問題所在,分析程序到底哪裏慢。
我在10.100.52.164上搭建了測試環境的服務端,和引擎團隊在10.100.51.151上搭建的客戶端端進行打通,完成對線上環境的模擬。環境搭建好了,接着我使用tcpcopy將線上機器的流量引入到了個人測試環境。流量打通以後,查看監控平臺,發現隨着流量的擴大,問題立刻暴露了出來:目前qps在1w的狀況下,客戶端rt達到了17ms左右。
測試環境與線上環境的現象基本一致:qps較大的狀況下,客戶端rt變得很是高,服務端rt幾乎爲0。因此瓶頸應該在客戶端。接着,咱們能夠利用火焰圖來分析測試環境下客戶端哪一個步驟比較慢
根據火焰圖能夠定位到統計調用時間的代碼處,MonitorFilter類的invoke方法。既然是從這裏統計到延遲較高,那麼問題確定出如今invoke調用鏈裏面的某個方法。根據火焰圖繼續往上分析調用棧,看到左邊的DefaultFuture的get方法和右邊OneToOneEncoder的doEncode方法各佔了很大一部分比例。那麼這兩個方法究竟是幹什麼的呢?
DefaultFuture.get:客戶端同步等待服務端響應。因爲dubbo協議採用的是netty異步寫,而後同步等待服務端響應的一種模式。因此這裏至關於客戶端等着服務端完成本地調用以後將執行結果返回回來的一個過程。
OneToOneEncoder.doEncode:客戶端編碼。這個步驟主要也就是對參數、方法名、接口名等信息的序列化操做。
使用btrace分別測出兩個方法的執行時間的分佈圖
時間大部分集中在16ms,在客戶端17ms的延遲表現中佔了絕大部分
時間大部分集中在0ms
發現doEncode雖然對cpu利用的較多,可是不怎麼消耗時間。真正消耗時間的是get方法。能夠經過一張圖來了解get操做等待的時候,後臺到底作了哪些操做。
因此程序頗有多是卡在網絡讀寫上面。
文章前部分有一張使用nicstat抓取的網絡讀寫狀態,發現網絡利用率很低,也就是網絡讀寫速度都很慢。可是使用ping網絡發現網絡速度並不慢,可是爲何在程序中,qps較大時,網絡讀寫速度就會很慢?
若是是網絡堵塞而致使速度很慢。那麼也就是客戶端的發送窗口和服務端接口窗口設置的過小,或者客戶端TCP發送緩存和服務端TCP接收緩存過小,當客戶端發起大量數據請求時,服務端沒法及時處理這些數據,那麼服務端就會選擇性的丟掉一部分包。可是根據上圖nicstat截圖發現,幾乎沒有產生丟包現象,並且我本身也嘗試過調大這些參數,發現仍是沒有什麼做用。因此這種可能性能夠排除。
翻閱《TCP/IP詳解(協議)》以後,查得還有一種可能性會致使網絡速度很慢,就是TCP的擁塞控制。爲了減小丟包而引起的性能損失,它會先預估線路中的擁擠狀況,而後來控制客戶端發送的流量。這極可能就是致使網路速度提不起來的一個關鍵因素。並且,目前使用dubbo協議的默認單一長鏈接模式,也就是隻有一個網絡讀寫通道。當這個通道某個方向的網絡傳輸量大了以後,就容易引發堵塞,TCP協議爲了避免產生堵塞而丟包,就進而控制了客戶端的數據傳送速度。
既然是一條線路的傳輸量太大而致使被"限速",那麼能夠試試開闢多條線路。也就是將原來客戶端-服務端單一長鏈接模式改爲客戶端-服務端多長鏈接模式。
在測試環境下,加大了鏈接個數以後,測試環境的延遲下降了。而在線上環境,改爲多長鏈接以後,在qps不變的狀況下,延遲從平均10ms降到了平均1ms。
下面是穩定以後,監控平臺記錄的線上環境的qps和rt數據
發現增長了長鏈接個數以後,延遲下降了,性能提高了。不過因爲目前每臺服務端監聽14臺客戶端,客戶端每增長一個長鏈接就會致使服務端長鏈接增長14個,若是鏈接過多,就會由於帶寬資源不夠而出現瓶頸,因此要根據線上實際狀況來調整長鏈接個數。
調用完成了,可是思考尚未中止。經過此次經歷,也算髮現了dubbo的一些弊端。做爲一款致力於提供服務的框架,dubbo的問題發現能力還有待完善。
綜上所述,dubbo還有值得完善,後續能夠對dubbo這些不足作一些擴展