1. 程式人生 > >從dubbo原始碼分析qos-server埠衝突問題

從dubbo原始碼分析qos-server埠衝突問題

在這分散式系統架構盛行的時代,很多網際網路大佬公司開源出自己的分散式RPC系統框架,例如:阿里的dubbo,谷歌的gRPC,apache的Thrift。而在我們公司一直都在推薦使用dubbo,今天就來講講在使用dubbo過程出現的qos-server埠衝突問題。

首先什麼是dubbo的qos-server呢?qos是dubbo的線上運維命令,dubbo2.5.8新版本重構了telnet模組,提供了新的telnet命令支援,新版本的telnet埠與dubbo協議的埠是不同的埠,預設為22222,可以通過配置檔案dubbo.properties修改。

在dubbo2.5.7版本之前是不支援註解配置的,因此公司框架Titans對dubbo做了二次封裝,通過自定義註解@EnableDuubo來支援dubbo的註解配置,並簡化了server provider,server consumer,server registry配置,開發人員只要在spring boot啟動類中新增@EnableDuubo即可開始自己的業務程式碼開發,節省大量的時間。

最近在專案開發過程中由於需要除錯,在本地同一臺機器上開了兩個執行例項,一個是dubbo provider例項,一個是dubbo consumer例項。首先啟動provider例項沒有任務問題,當啟動consumer以後,控制檯卻丟擲如下問題,

為了解決埠衝突,開始在專案中使用各種idea搜尋快捷鍵搜尋關鍵字qos-server和22222都沒有搜尋到任何資訊。一般日誌都會列印包名和型別,便於快速定位問題,然後這裡日誌列印根本沒有輸出包名和類,趕緊修改日誌列印格式配置,終於得到如下錯誤資訊

原來是這個錯誤是dubbo的com.alibaba.dubbo.qos.server的Server類中打印出來,檢視Server類原始碼發現在方法start中呼叫了netty的初始化方法,並將port作為被外部訪問的qos-server埠,程式碼如下

那麼問題來了:

問題1. 什麼時候會啟用qos服務呢,埠是何時被賦值的呢?

在Server類中可以看到唯一為 port引數賦值的方法只有setPor()方法,檢視發現也只有在QosProtocolWrapper中呼叫了setPort()方法,檢視原始碼可以發現,在該類的startQosServer()方法中會首先從url中解析qos.enable引數,如果未獲取到該引數值,則預設啟動qos服務,同時服務埠也是從url中解析,預設使用22222。

問題2. 那麼這個url到底是啥呢?

其實斷點程式可以發現,該url就是專案中對dubbo的一些配置資訊,例如註冊中心地址,超時時間,超時重試次數等等,由於專案中沒有對qos服務的配置,因此全部採用了預設配置,導致埠衝突。

問題3. 如何禁用qos服務呢?

雖然qos埠衝突並影響服務消費者消費服務,但是每次程式啟動總是丟擲埠衝突異常,有強迫證的程式肯定以為程式哪裡出錯了,總會有那麼一點忐忑。而且大多數情況可能並不需要這個qos服務,預設開啟浪費埠,浪費機器資源(雖然資源佔用並不一定很多),如果你是 java 註解配置,可以通過如下程式碼禁用qos服務,

或者通過通過dubbo官網已經給出了文件來配置,文件地址 http://dubbo.apache.org/#!/docs/user/references/qos.md?lang=zh-cn

問題4. 為什麼我獨立搭建dubbo分散式服務的時候沒有出現qos-server埠衝突呢?

原因在於qos-server 需要netty4版本的支援,預設情況下dubbo不會引用netty4的依賴包,(而專案中有依賴netty4,因此丟擲埠異常,)因此在QosProtocolWrapper類中呼叫Server類的start()方法啟動qos服務時,會丟擲ClassNotFoundException,QosProtocolWrapper的startQosServer()方法僅僅try catch了異常,未做任何處理,因此根本沒有丟擲任何異常,這是極其反對的一種做法,至少打個日誌啊。原始碼如下:

總結:

1. 開發過程中日誌列印及日誌列印格式是非常重要的,可能大大減少問題定位的時間,因此一定要注意有效的日誌輸出

2. 要學會解決問題的思路,埠衝突是一個很常見的問題,通過百度或者谷歌搜尋可能幾分鐘就能解決,只是想借此來更多的瞭解框架的實現細節,知其然知其所以然。