Dubbo 新編程模型之外部化配置

外部化配置(External Configuration)

在Dubbo 註解驅動例子中,無論是服務提供方,還是服務消費方,均需要轉配相關配置Bean:

@Beannpublic ApplicationConfig applicationConfig() {nApplicationConfig applicationConfig = new ApplicationConfig();n applicationConfig.setName("dubbo-annotation-consumer");nreturn applicationConfig;n}n

雖然實現類似於 ProviderConfigurationConsumerConfiguration 這樣的 Spring @Configuration Bean 成本並不高,不過通過 Java Code 的方式定義配置 Bean,或多或少是一種 Hard Code(硬編碼)的行為,缺少彈性。

儘管在 Spring 應用中,可以通過 @Value 或者 Environment 的方式獲取外部配置,其代碼簡潔性以及類型轉換靈活性存在明顯的不足。因此,Spring Boot 提出了外部化配置(External Configuration)的感念,即通過程序以外的配置源,動態地綁定指定類型。

隨著 Spring Boot / Spring Cloud 應用的流行,開發人員逐漸地接受並且使用 Spring Boot 外部化配置(External Configuration),即通過 application.properties 或者 bootstrap.properties 裝配配置 Bean。

下列表格記錄了 Dubbo 內置配置類:

通過申明對應的 Spring 擴展標籤,在 Spring 應用上下文中將自動生成相應的配置 Bean。

在 Dubbo 官方用戶手冊的「屬性配置」章節中, dubbo.properties 配置屬性能夠映射到 ApplicationConfigProtocolConfig 以及 RegistryConfig 的欄位。從某種意義上來說, dubbo.properties 也是 Dubbo 的外部化配置。

其中,引用「映射規則」的內容:

映射規則

將 XML 配置的標籤名,加屬性名,用點分隔,多個屬性拆成多行

  • 比如: dubbo.application.name=foo等價於

<dubbo:applicationname="foo"/> n

  • 比如: dubbo.registry.address=10.20.153.10:9090等價於

<dubbo:registryaddress="10.20.153.10:9090"/>n

如果 XML 有多行同名標籤配置,可用 id 號區分,如果沒有 id 號將對所有同名標籤生效

  • 比如: dubbo.protocol.rmi.port=1234等價於 <dubbo:protocolid="rmi"name="rmi"port="1099"/>2
  • 比如: dubbo.registry.china.address=10.20.153.10:9090等價於 <dubbo:registryid="china"address="10.20.153.10:9090"/>

下面是 dubbo.properties 的一個典型配置:

dubbo.application.name=foondubbo.application.owner=barndubbo.registry.address=10.20.153.10:9090n

根據「映射規則」,Dubbo 即支持單配置 Bean 映射,也支持多 Bean 映射。綜合以上需求,既要兼容 Dubbo 已有的一個或多個 Bean 欄位映射綁定,也支持外部化配置。

特別提醒:外部化配置(External Configuration)並非 Spring Boot 特有,即使在 Spring Framework 場景下亦能支持。也就是說 Dubbo 外部化配置即可在 Spring Framework 中工作,也能在 Spring Boot 中運行。

Dubbo 外部化配置(External Configuration) 支持起始版本為: 2.5.8

@EnableDubboConfig

起始版本: 2.5.8

使用說明

@EnableDubboConfig 定義

public @interface EnableDubboConfig {nn/**n * It indicates whether binding to multiple Spring Beans.n *n * @return the default value is <code>false</code>n * @revised 2.5.9n */nboolean multiple() default false;nn}n

  • multiple : 表示是否支持多Dubbo 配置 Bean 綁定。默認值為 false ,即單 Dubbo 配置 Bean 綁定

單 Dubbo 配置 Bean 綁定

為了更好地向下兼容, @EnableDubboConfig 提供外部化配置屬性與 Dubbo 配置類之間的綁定,其中映射關係如下:

當標註 @EnableDubboConfig 的類被掃描註冊後,同時 Spring(Spring Boot)應用配置( PropertySources)中存在 dubbo.application.* 時, ApplicationConfig Bean 將被註冊到在 Spring 上下文。否則,不會被註冊。如果出現 dubbo.registry.*的配置,那麼, RegistryConfig Bean 將會創建,以此類推。即按需裝配 Dubbo 配置 Bean。

如果需要指定配置 Bean的 id,可通過 **.id 屬性設置,以 dubbo.application 為例:

## applicationndubbo.application.id = applicationBeanndubbo.application.name = dubbo-demo-applicationn

以上配置等同於以下 Java Config Bean:

@Bean("applicationBean")npublic ApplicationConfig applicationBean() {nApplicationConfig applicationConfig = new ApplicationConfig();n applicationConfig.setName("dubbo-demo-application");nreturn applicationConfig;n}n

大致上配置屬性與配置類綁定模式 - dubbo.application.* 映射到 ApplicationConfig 中的欄位。

註:當配置屬性名稱無法在配置類中找到欄位時,將會忽略綁定

多 Dubbo 配置 Bean 綁定

Dubbo @Service@Reference 允許 Dubbo 應用關聯 ApplicationConfig Bean 或者指定多個 RegistryConfig Bean 等能力。換句話說,Dubbo 應用上下文中可能存在多個 ApplicationConfig 等 Bean定義。

為了適應以上需要,因此從Dubbo 2.5.9 開始, @EnableDubboConfig 支持多 Dubbo 配置 Bean 綁定,同時按照業界規約標準,與單 Dubbo 配置 Bean 綁定約定不同,配置屬性前綴均為英文複數形式:

詳情請參考 :github.com/alibaba/dubb

  • dubbo.applications
  • dubbo.modules
  • dubbo.registries
  • dubbo.protocols
  • dubbo.monitors
  • dubbo.providers
  • dubbo.consumers

dubbo.applications 為例,基本的模式如下:

dubbo.applications.${bean-name}.property-name = ${property-value}n

請讀者注意,在單 Dubbo 配置 Bean 綁定時,可以通過指定 id 屬性的方式,定義 ApplicationConfig Bean 的ID,即 dubbo.application.id

而在多 Dubbo 配置 Bean 綁定時,Bean ID 則由 dubbo.applications.與屬性欄位名稱( .property-name)之間的字元來表達。

如下配置:

# multiple Bean definitionndubbo.applications.applicationBean.name = dubbo-demo-applicationndubbo.applications.applicationBean2.name = dubbo-demo-application2ndubbo.applications.applicationBean3.name = dubbo-demo-application3n

該配置內容中,綁定了三個 ApplicationConfig Bean,分別是 applicationBeanapplicationBean2以及 applicationBean3

示例說明

@EnableDubboConfig 的使用方法很簡答, 再次強調一點,當規約的外部配置存在時,相應的 Dubbo 配置類 才會提升為 Spring Bean。簡言之,按需裝配。

單 Dubbo 配置 Bean 綁定

外部化配置文件

將以下內容的外部化配置文件物理路徑為: classpath:/META-INF/config.properties:

# Dubbo 配置 Bean 綁定n## applicationndubbo.application.id = applicationBeanndubbo.application.name = dubbo-demo-applicationnn## modulendubbo.module.id = moduleBeanndubbo.module.name = dubbo-demo-modulenn## registryndubbo.registry.address = zookeeper://192.168.99.100:32770nn## protocolndubbo.protocol.name = dubbondubbo.protocol.port = 20880nn## monitorndubbo.monitor.address = zookeeper://127.0.0.1:32770nn## providerndubbo.provider.host = 127.0.0.1nn## consumerndubbo.consumer.client = nettyn

@EnableDubboConfig 配置 Bean

/**n* Dubbo 配置 Beann*n* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>n*/n@EnableDubboConfign@PropertySource("META-INF/config.properties")n@Configurationnpublic class DubboConfiguration {nn}n

實現引導類

/**n* Dubbo 配置引導類n*n* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>n*/npublic class DubboConfigurationBootstrap {nnpublic static void main(String[] args) {n// 創建配置上下文nAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();n// 註冊當前配置 Beann context.register(DubboConfiguration.class);n context.refresh();n// applicationnApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class);nSystem.out.printf("applicationBean.name = %s n", applicationConfig.getName());nn// modulenModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class);nSystem.out.printf("moduleBean.name = %s n", moduleConfig.getName());nn// registrynRegistryConfig registryConfig = context.getBean(RegistryConfig.class);nSystem.out.printf("registryConfig.name = %s n", registryConfig.getAddress());nn// protocolnProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class);nSystem.out.printf("protocolConfig.name = %s n", protocolConfig.getName());nSystem.out.printf("protocolConfig.port = %s n", protocolConfig.getPort());nn// monitornMonitorConfig monitorConfig = context.getBean(MonitorConfig.class);nSystem.out.printf("monitorConfig.name = %s n", monitorConfig.getAddress());nn// providernProviderConfig providerConfig = context.getBean(ProviderConfig.class);nSystem.out.printf("providerConfig.name = %s n", providerConfig.getHost());nn// consumernConsumerConfig consumerConfig = context.getBean(ConsumerConfig.class);nSystem.out.printf("consumerConfig.name = %s n", consumerConfig.getClient());n}n}n

執行結果

applicationBean.name = dubbo-demo-application nmoduleBean.name = dubbo-demo-module nregistryConfig.name = zookeeper://192.168.99.100:32770 nprotocolConfig.name = dubbo nprotocolConfig.port = 20880 nmonitorConfig.name = zookeeper://127.0.0.1:32770 nproviderConfig.name = 127.0.0.1 nconsumerConfig.name = netty n

不難發現, @EnableDubboConfig 配置 Bean 配合外部化文件 classpath:/META-INF/config.properties,與執行輸出內容相同。

多 Dubbo 配置 Bean 綁定

外部化配置文件

將以下內容的外部化配置文件物理路徑為: classpath:/META-INF/multiple-config.properties:

# Dubbo 配置 Bean 綁定n## dubbo.applicationsndubbo.applications.applicationBean.name = dubbo-demo-applicationndubbo.applications.applicationBean2.name = dubbo-demo-application2ndubbo.applications.applicationBean3.name = dubbo-demo-application3n

@EnableDubboConfig 配置 Bean(多)

@EnableDubboConfig(multiple = true)n@PropertySource("META-INF/multiple-config.properties")nprivate static class DubboMultipleConfiguration {nn} n

實現引導類

/**n* Dubbo 配置引導類n*n* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>n*/npublic class DubboConfigurationBootstrap {npublic static void main(String[] args) {n// 創建配置上下文nAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();n// 註冊當前配置 Beann context.register(DubboMultipleConfiguration.class);n context.refresh();nn// 獲取 ApplicationConfig Bean:"applicationBean"、"applicationBean2" 和 "applicationBean3"nApplicationConfig applicationBean = context.getBean("applicationBean", ApplicationConfig.class);nApplicationConfig applicationBean2 = context.getBean("applicationBean2", ApplicationConfig.class);nApplicationConfig applicationBean3 = context.getBean("applicationBean3", ApplicationConfig.class);nnSystem.out.printf("applicationBean.name = %s n", applicationBean.getName());nSystem.out.printf("applicationBean2.name = %s n", applicationBean2.getName());nSystem.out.printf("applicationBean3.name = %s n", applicationBean3.getName());n}n}n

執行結果

applicationBean.name = dubbo-demo-application napplicationBean2.name = dubbo-demo-application2 napplicationBean3.name = dubbo-demo-application3 n

@EnableDubboConfig(multiple=true) 執行後,運行結果說明 ApplicationConfig Bean 以及 ID 的定義方式。

@EnableDubboConfigBinding & @EnableDubboConfigBindings

@EnableDubboConfig適合絕大多數外部化配置場景,然而無論是單 Bean 綁定,還是多 Bean 綁定,其外部化配置屬性前綴是固化的,如 dubbo.application 以及 dubbo.applications

當應用需要自定義外部化配置屬性前綴@EnableDubboConfigBinding能提供更大的彈性,支持單個外部化配置屬性前綴( prefix) 與 Dubbo 配置 Bean 類型( AbstractConfig 子類)綁定,如果需要多次綁定時,可使用 @EnableDubboConfigBindings

儘管 Dubbo 推薦使用 Java 8 ,然而實際的情況,運行時的 JDK 的版本可能從 6到8 均有。因此, @EnableDubboConfigBinding 沒有實現 java.lang.annotation.Repeatable,即允許實現類不支持重複標註 @EnableDubboConfigBinding

@EnableDubboConfigBinding 在支持外部化配置屬性與 Dubbo 配置類綁定時,與 Dubbo 過去的映射行為不同,被綁定的 Dubbo 配置類將會提升為 Spring Bean,無需提前裝配 Dubbo 配置類。同時,支持多 Dubbo 配置Bean 裝配。其 Bean 的綁定規則與 @EnableDubboConfig一致。

起始版本: 2.5.8

使用說明

@EnableDubboConfigBinding 定義

public @interface EnableDubboConfigBinding {nn/**n * The name prefix of the properties that are valid to bind to {@link AbstractConfig Dubbo Config}.n *n * @return the name prefix of the properties to bindn */nString prefix();nn/**n * @return The binding type of {@link AbstractConfig Dubbo Config}.n * @see AbstractConfign * @see ApplicationConfign * @see ModuleConfign * @see RegistryConfign */nClass<? extends AbstractConfig> type();nn/**n * It indicates whether {@link #prefix()} binding to multiple Spring Beans.n *n * @return the default value is <code>false</code>n */nboolean multiple() default false;nn}n

  • prefix() : 指定待綁定 Dubbo 配置類的外部化配置屬性的前綴,比如 dubbo.applicationApplicationConfig 的外部化配置屬性的前綴。 prefix() 支持佔位符(Placeholder), 並且其關聯前綴值是否以"." 作為結尾字元是可選的,即 prefix()="dubbo.application"prefix()="dubbo.application."效果相同
  • type() : 指定 Dubbo 配置類,所有 AbstractConfig 的實現子類即可,如 ApplicationConfigRegistryConfig 以及 ProtocolConfig
  • multiple() : 表明是否需要將 prefix() 作為多個 type() 類型的 Spring Bean 外部化配置屬性。默認值為 false,即默認支持單個類型的 Spring 配置 Bean

假設標註 @EnableDubboConfigBinding 的實現類被 Spring 應用上下文掃描並且註冊後,其中 prefix() = dubbo.apptype() = ApplicationConfig.class ,且外部配置內容為:

dubbo.app.id = applicationBeanndubbo.app.name = dubbo-demo-applicationn

Spring 應用上下文啟動後,一個 ID 為 "applicationBean" 的 ApplicationConfig Bean 被初始化,其 name 欄位被設置為 "dubbo-demo-application"。

EnableDubboConfigBindings 定義

public @interface EnableDubboConfigBindings {nn/**n * The value of {@link EnableDubboConfigBindings}n *n * @return non-nulln */nEnableDubboConfigBinding[] value();nn}n

  • value : 指定多個 EnableDubboConfigBinding,用於實現外部化配置屬性前綴( prefix) 與 Dubbo 配置 Bean 類型( AbstractConfig 子類)綁定。

示例說明

外部化配置文件

將以下內容的外部化配置文件物理路徑為: classpath:/META-INF/bindings.properties

# classpath:/META-INF/bindings.propertiesn## 佔位符值 : ApplicationConfig 外部配置屬性前綴napplications.prefix = dubbo.apps.nn## ApplicationConfig Bean 綁定ndubbo.apps.applicationBean.name = dubbo-demo-applicationndubbo.apps.applicationBean2.name = dubbo-demo-application2ndubbo.apps.applicationBean3.name = dubbo-demo-application3nn## ModuleConfig Bean 綁定ndubbo.module.id = moduleBeanndubbo.module.name = dubbo-demo-modulenn## RegistryConfig Bean 綁定ndubbo.registry.address = zookeeper://192.168.99.100:32770n

EnableDubboConfigBindings 配置 Bean

DubboConfiguration 作為 Dubbo 配置 Bean,除通過 @EnableDubboConfigBinding 綁定之外,還需要 @PropertySource 指定外部化配置文件( classpath:/META-INF/bindings.properties):

/**n* Dubbo 配置 Beann*n* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>n*/n@EnableDubboConfigBindings({n@EnableDubboConfigBinding(prefix = "${applications.prefix}",n type = ApplicationConfig.class, multiple = true), // 多 ApplicationConfig Bean 綁定n@EnableDubboConfigBinding(prefix = "dubbo.module", // 不帶 "." 後綴n type = ModuleConfig.class), // 單 ModuleConfig Bean 綁定n@EnableDubboConfigBinding(prefix = "dubbo.registry.", // 帶 "." 後綴n type = RegistryConfig.class) // 單 RegistryConfig Bean 綁定n})n@PropertySource("META-INF/bindings.properties")n@Configurationnpublic class DubboConfiguration {nn}n

實現引導類

通過之前的使用說明,當 EnableDubboConfigBinding 將外部配置化文件 classpath:/META-INF/dubbo.properties 綁定到 ApplicationConfig後,其中 Spring Bean "applicationBean" 的 name 欄位被設置成 "dubbo-demo-application"。同時, EnableDubboConfigBinding 所標註的 DubboConfiguration 需要被 Sring 應用上下文註冊:

/**n* Dubbo 配置引導類n*n* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>n*/npublic class DubboConfigurationBootstrap {nnpublic static void main(String[] args) {n// 創建配置上下文nAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();n// 註冊當前配置 Beann context.register(DubboConfiguration.class);n context.refresh();n// 獲取 ApplicationConfig Bean:"applicationBean"、"applicationBean2" 和 "applicationBean3"nApplicationConfig applicationBean = context.getBean("applicationBean", ApplicationConfig.class);nApplicationConfig applicationBean2 = context.getBean("applicationBean2", ApplicationConfig.class);nApplicationConfig applicationBean3 = context.getBean("applicationBean3", ApplicationConfig.class);nnSystem.out.printf("applicationBean.name = %s n", applicationBean.getName());nSystem.out.printf("applicationBean2.name = %s n", applicationBean2.getName());nSystem.out.printf("applicationBean3.name = %s n", applicationBean3.getName());nn// 獲取 ModuleConfig Bean:"moduleBean"nModuleConfig moduleBean = context.getBean("moduleBean", ModuleConfig.class);nnSystem.out.printf("moduleBean.name = %s n", moduleBean.getName());nn// 獲取 RegistryConfig BeannRegistryConfig registry = context.getBean(RegistryConfig.class);nnSystem.out.printf("registry.address = %s n", registry.getAddress());n}n}n

運行結果

DubboConfigurationBootstrap 運行後控制台輸出:

applicationBean.name = dubbo-demo-application napplicationBean2.name = dubbo-demo-application2 napplicationBean3.name = dubbo-demo-application3 nmoduleBean.name = dubbo-demo-module nregistry.address = zookeeper://192.168.99.100:32770 n

輸出的內容與 classpath:/META-INF/bindings.properties 綁定的內容一致,符合期望。

關於作者

小馬哥,十餘年Java EE 從業經驗,架構師、微服務佈道師、Dubbo 維護者。目前主要負責阿里巴巴集團微服務技術實施、架構衍進、基礎設施構建等。重點關注云計算、微服務以及軟體架構等領域。通過SUN Java(SCJP、SCWCD、SCBCD)以及Oracle OCA 等的認證。

作者:小馬哥

鏈接:Dubbo 新編程模型之外部化配置

聲明:本文來源於Spring for All,版權歸作者所有,有什麼問題,請聯繫我們,謝謝!


推薦閱讀:

有沒有人能對twitter的finagle和國內的dubbo做個對比?
分散式服務治理的設計問題?

TAG:dubbo | 负载均衡 |