1. 程式人生 > >Dubbo配置設計

Dubbo配置設計

ride sts img TE per 通過 apach 以及 另一個

  1. 配置分類
  2. 配置格式
  3. 配置加載
  4. 可編程配置
  5. 配置缺省值
  6. 配置一致性
  7. 配置覆蓋
  8. 配置繼承
  9. 配置向後兼容

配置分類

首先,配置的用途是有多種的,大致可以分為:

  1. 環境配置,比如:連接數,超時等配置。
  2. 描述配置,比如:服務接口描述,服務版本等。
  3. 擴展配置,比如:協議擴展,策略擴展等。

配置格式

通常環境配置,用 properties 配置會比較方便,因為都是一些離散的簡單值,用 key-value 配置可以減少配置的學習成本。

而描述配置,通常信息比較多,甚至有層次關系,用 xml 配置會比較方便,因為樹結構的配置表現力更強。如果非常復雜,也可以考自定義 DSL 做為配置。有時候這類配置也可以用 Annotation 代替, 因為這些配置和業務邏輯相關,放在代碼裏也是合理的。

另外擴展配置,可能不盡相同。如果只是策略接口實現類替換,可以考慮 properties 等結構。如果有復雜的生命周期管理,可能需要 XML 等配置。有時候擴展會通過註冊接口的方式提供。

配置加載

對於環境配置,在 java 世界裏,比較常規的做法,是在 classpath 下約定一個以項目為名稱的 properties 配置,比如:log4j.properties,velocity.properties等。產品在初始化時,自動從 classpath 下加載該配置。我們平臺的很多項目也使用類似策略,如:dubbo.properties,comsat.xml 等。這樣有它的優勢,就是基於約定,簡化了用戶對配置加載過程的幹預。但同樣有它的缺點,當 classpath 存在同樣的配置時,可能誤加載,以及在 ClassLoader 隔離時,可能找不到配置,並且,當用戶希望將配置放到統一的目錄時,不太方便。

Dubbo 新版本去掉了 dubbo.properties,因為該約定經常造成配置沖突。

而對於描述配置,因為要參與業務邏輯,通常會嵌到應用的生命周期管理中。現在使用 spring 的項目越來越多,直接使用 spring 配置的比較普遍,而且 spring 允許自定義 schema,配置簡化後很方便。當然,也有它的缺點,就是強依賴 spring,可以提編程接口做了配套方案。

在 Dubbo 即存在描述配置,也有環境配置。一部分用 spring 的 schame 配置加載,一部分從 classpath 掃描 properties 配置加載。用戶感覺非常不便,所以在新版本中進行了合並,統一放到 spring 的 schame 配置加載,也增加了配置的靈活性。

擴展配置,通常對配置的聚合要求比較高。因為產品需要發現第三方實現,將其加入產品內部。在 java 世界裏,通常是約定在每個 jar 包下放一個指定文件加載,比如:eclipse 的 plugin.xml,struts2 的 struts-plugin.xml 等,這類配置可以考慮 java 標準的服務發現機制,即在 jar 包的 META-INF/services 下放置接口類全名文件,內容為每行一個實現類類名,就像 jdk 中的加密算法擴展,腳本引擎擴展,新的 JDBC 驅動等,都是采用這種方式。參見:ServiceProvider 規範。

Dubbo 舊版本通過約定在每個 jar 包下,放置名為 dubbo-context.xml 的 spring 配置進行擴展與集成,新版本改成用 jdk 自帶的 META-INF/services 方式,去掉過多的 spring 依賴。

可編程配置

配置的可編程性是非常必要的,不管你以何種方式加載配置文件,都應該提供一個編程的配置方式,允許用戶不使用配置文件,直接用代碼完成配置過程。因為一個產品,尤其是組件類產品,通常需要和其它產品協作使用,當用戶集成你的產品時,可能需要適配配置方式。

Dubbo 新版本提供了與 xml 配置一對一的配置類,如:ServiceConfig 對應 <dubbo:service />,並且屬性也一對一,這樣有利於文件配置與編程配置的一致性理解,減少學習成本。

配置缺省值

配置的缺省值,通常是設置一個常規環境的合理值,這樣可以減少用戶的配置量。通常建議以線上環境為參考值,開發環境可以通過修改配置適應。缺省值的設置,最好在最外層的配置加載就做處理。程序底層如果發現配置不正確,就應該直接報錯,容錯在最外層做。如果在程序底層使用時,發現配置值不合理,就填一個缺省值,很容易掩蓋表面問題,而引發更深層次的問題。並且配置的中間傳遞層,很可能並不知道底層使用了一個缺省值,一些中間的檢測條件就可能失效。Dubbo 就出現過這樣的問題,中間層用“地址”做為緩存 Key, 而底層,給“地址”加了一個缺省端口號,導致不加端口號的“地址”和加了缺省端口的“地址”並沒有使用相同的緩存。

配置一致性

配置總會隱含一些風格或潛規則,應盡可能保持其一致性。比如:很多功能都有開關,然後有一個配置值:

  1. 是否使用註冊中心,註冊中心地址。
  2. 是否允許重試,重試次數。

你可以約定:

  1. 每個都是先配置一個 boolean 類型的開關,再配置一個值。
  2. 用一個無效值代表關閉,N/A地址,0重試次數等。

不管選哪種方式,所有配置項,都應保持同一風格,Dubbo 選的是第二種。相似的還有,超時時間,重試時間,定時器間隔時間。如果一個單位是秒,另一個單位是毫秒(C3P0的配置項就是這樣),配置人員會瘋掉。

配置覆蓋

提供配置時,要同時考慮開發人員,測試人員,配管人員,系統管理員。測試人員是不能修改代碼的,而測試的環境很可能較為復雜,需要為測試人員留一些“後門”,可以在外圍修改配置項。就像 spring 的 PropertyPlaceholderConfigurer 配置,支持 SYSTEM_PROPERTIES_MODE_OVERRIDE,可以通過 JVM 的 -D 參數,或者像 hosts 一樣約定一個覆蓋配置文件,在程序外部,修改部分配置,便於測試。

Dubbo 支持通過 JVM 參數 -Dcom.xxx.XxxService=dubbo://10.1.1.1:1234 直接使遠程服務調用繞過註冊中心,進行點對點測試。還有一種情況,開發人員增加配置時,都會按線上的部署情況做配置,如:<dubbo:registry address="${dubbo.registry.address}" /> 因為線上只有一個註冊中心,這樣的配置是沒有問題的,而測試環境可能有兩個註冊中心,測試人員不可能去修改配置,改為: <dubbo:registry address="${dubbo.registry.address1}" /><dubbo:registry address="${dubbo.registry.address2}" />,所以這個地方,Dubbo 支持在 ${dubbo.registry.address} 的值中,通過豎號分隔多個註冊中心地址,用於表示多註冊中心地址。

配置繼承

配置也存在“重復代碼”,也存在“泛化與精化”的問題。比如:Dubbo 的超時時間設置,每個服務,每個方法,都應該可以設置超時時間。但很多服務不關心超時,如果要求每個方法都配置,是不現實的。所以 Dubbo 采用了方法超時繼承服務超時,服務超時再繼承缺省超時,沒配置時,一層層向上查找。

另外,Dubbo 舊版本所有的超時時間,重試次數,負載均衡策略等都只能在服務消費方配置。但實際使用過程中發現,服務提供方比消費方更清楚,但這些配置項是在消費方執行時才用到的。新版本,就加入了在服務提供方也能配這些參數,通過註冊中心傳遞到消費方, 做為參考值,如果消費方沒有配置,就以提供方的配置為準,相當於消費方繼承了提供方的建議配置值。而註冊中心在傳遞配置時,也可以在中途修改配置,這樣就達到了治理的目的,繼承關系相當於:服務消費者 --> 註冊中心 --> 服務提供者

技術分享圖片

配置向後兼容

向前兼容很好辦,你只要保證配置只增不減,就基本上能保證向前兼容。但向後兼容,也是要註意的,要為後續加入新的配置項做好準備。如果配置出現一個特殊配置,就應該為這個“特殊”情況約定一個兼容規則,因為這個特殊情況,很有可能在以後還會發生。比如:有一個配置文件是保存“服務=地址”映射關系的,其中有一行特殊,保存的是“註冊中心=地址”。現在程序加載時,約定“註冊中心”這個Key是特殊的,做特別處理,其它的都是“服務”。然而,新版本發現,要加一項“監控中心=地址”,這時,舊版本的程序會把“監控中心”做為“服務”處理,因為舊代碼是不能改的,兼容性就很會很麻煩。如果先前約定“特殊標識+XXX”為特殊處理,後續就會方便很多。

Dubbo配置設計