栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

dubbo之外部化配置

dubbo之外部化配置

号外号外:麻烦点击左侧我正在参加博客之星评选,请您投票,给个五星好评,谢谢大家了!!!

写在前面

dubbo提供了ApplicationConfig,ProtocolConfig,ProviderConfig,ConsumerConfig,RegistryConfig等类,来完成dubbo各种信息的注册工作,当前只支持的如下图:

目前dubbo主要支持的配置方式有xml配置,注解配置(使用@DubboComponentScan+@Configuration方式),另外还有一种配置方式,那就是外部化配置,外部化配置是springboot提出的一种全新的配置理念,通过配置的信息来按照指定的规则生成spring bean,从而实现进一步简化用户配置和使用复杂度的目的,在我们使用springboot时,一般使用的也都是这种方式,dubbo在2.5.8版本也正式开始支持外部化配置,对应的注解是@EnableDubboConfig,其源码如下:

// 将配置文件中约定前缀的属性自动映射为对应的XxxxConfig类,如
// ApplicationConfig绑定到的属性是dubbo.application,如dubbo.application.name=foo等价于
// ModuleConfig绑定到的属性是dubbo.module
// RegistryConfig绑定到的属性是dubbo.registry,如dubbo.registry.address=192.168.10.119:2181等价于
// ProtocolConfig绑定到的属性是dubbo.protocol,如dubbo.protocol.rmi.port=1234等价于
// MonitorConfig绑定到的属性是dubbo.monitor
// ProviderConfig绑定到的属性是dubbo.provider
// ConsumerConfig绑定到的属性是dubbo.consumer
// 如果是需要同时绑定一个外部化配置到多个spring bean,则需要将multiple()方法返回true
// ApplicationConfig绑定到的属性是dubbo.applications
// ModuleConfig绑定到的属性是dubbo.modules
// RegistryConfig绑定到的属性是dubbo.registrys
// ProtocolConfig绑定到的属性是dubbo.protocols
// MonitorConfig绑定到的属性是dubbo.monitors
// ProviderConfig绑定到的属性是dubbo.providers
// ConsumerConfig绑定到的属性是dubbo.consumers
@Target({ElementType.TYPE}) // 用在类上的注解
@Retention(RetentionPolicy.RUNTIME) // 保留到运行时,即一直保留
@Inherited // 某个类使用了该注解标注的注解,则其子类会继承该注解,可参考:https://blog.csdn.net/wang0907/article/details/113131549#t0
@documented
@import(DubboConfigConfigurationRegistrar.class) // 导入spring bean
public @interface EnableDubboConfig {

    // 是否允许绑定到多个spring bean,从2.6.6版本,该值默认为true,如下是2.5.9中的配置:
    
    boolean multiple() default true;
}

如下是一个可能的外部化配置的例子:

dubbo.application.name=foo
dubbo.application.owner=bar
dubbo.registry.address=10.20.153.10:9090

需要注意外部化配置并非springboot所有特有,spring framework也是支持的,因此不管是在springboot的环境下还是在spring framework的环境下dubbo的外部化配置都是可以使用的,另外需要注意的是dubbo开始支持外部化配置的版本是2.5.8,在使用的时候一定要注意自己的版本。

接下来我们具体看下@EnableDubboConfig注解。

1:@EnableDubboConfig 1.1:起始版本

2.5.8。

源码如下前面已经贴过了,这里再贴一次:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@documented
@import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
    boolean multiple() default true;
}
1.2:单dubbo配置bean绑定

为了能够更好的分析,我们先来看一个完整的例子,源码 。

1.2.1:定义service接口和实现类
  • 接口
public interface MyExternalConfigService {
    String sayHi(String word);
}
  • 实现类
@Service
//@Component("myExternalConfigService")
public class MyExternalConfigServiceImpl implements MyExternalConfigService {
    @Override
    public String sayHi(String word) {
        return "external say hi: " + word;
    }
}
1.2.2:定义服务提供者端外部配置文件

externalconfig/externalConfigInfo.properties:

#
dubbo.application.name=dongshidaddy-provider
dubbo.application.owner=dongshidaddy
#
dubbo.registry.address=zookeeper://192.168.10.119:2181
#
dubbo.protocol.name=dubbo
dubbo.protocol.port=20827
#
dubbo.service.interface=dongshi.daddy.service.externalconfig.MyExternalConfigService
dubbo.service.ref=myExternalConfigService

相当于如下的xml配置:


    
    
    
    
    
    
    
    
    

相当于如下的java config配置:

@Configuration
@EnableDubbo(scanbasePackages = "dongshi.daddy.service.annotation")
public class DubboConfiguration {

    @Bean // #1 服务提供者信息配置
    public ProviderConfig providerConfig() {
        ProviderConfig providerConfig = new ProviderConfig();
        providerConfig.setTimeout(1000);
        return providerConfig;
    }

    @Bean // #2 分布式应用信息配置,相当于xml配置:
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("dubbo-annotation-provider");
        return applicationConfig;
    }

    @Bean // #3 注册中心信息配置,相当于xml配置:
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress("192.168.10.119");
        registryConfig.setPort(2181);
        return registryConfig;
    }

    @Bean // #4 使用协议配置,这里使用 dubbo,相当于xml配置:
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(20885);
        return protocolConfig;
    }
}
1.2.3:定义服务提供者端java config类
@Configuration
// 开启外部化配置
@EnableDubboConfig
// 配置扫描
@DubboComponentScan(basePackageClasses = { MyExternalConfigService.class })
@PropertySource("externalconfig/externalConfigInfo.properties")
public class MyExternalServiceConfiguration {
}
1.2.4:定义服务提供者端main类
public class ProviderWithExternlConfigMain {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyExternalServiceConfiguration.class);
        ApplicationConfig applicationConfig = context.getBean(ApplicationConfig.class);
        System.out.println("ApplicationConfig is: " + applicationConfig);
        RegistryConfig registryConfig = context.getBean(RegistryConfig.class);
        System.out.println("RegistryConfig is: " + registryConfig);
        ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class);
        System.out.println("ProtocolConfig is: " + protocolConfig);

        // 主进程不退出
        System.in.read();
    }
}

启动后在es中验证是否成功:

[zk: localhost:2181(CONNECTED) 2] ls /dubbo/dongshi.daddy.service.externalconfig.MyExternalConfigService/providers
[dubbo://192.168.64.1:20827/dongshi.daddy.service.externalconfig.MyExternalConfigService?anyhost=true&application=dongshidaddy-provider&bean.name=providers:dubbo:dongshi.daddy.service.externalconfig.MyExternalConfigService&dubbo=2.0.2&generic=false&interface=dongshi.daddy.service.externalconfig.MyExternalConfigService&methods=sayHi&owner=dongshidaddy&pid=23248&side=provider×tamp=1640315204607]
1.2.5:定义服务消费者外部配置文件

externalconsumerconfig/externalConsumerConfigInfo.properties:

#
dubbo.application.name=dongshidaddy-consumer
dubbo.application.owner=dongshidaddy
#
dubbo.registry.address=zookeeper://192.168.10.119:2181
1.2.6:定义服务消费者的服务提供者调用类
@Component("myExternalConfigConusmerService")
public class MyExternalConfigConusmerService {
    @Reference(interfaceClass = MyExternalConfigService.class)
    private MyExternalConfigService myExternalConfigService;

    public MyExternalConfigService returnClassProviderService() {
        return myExternalConfigService;
    }

    public MyExternalConfigService getMyExternalConfigService() {
        return myExternalConfigService;
    }

    public void setMyExternalConfigService(MyExternalConfigService myExternalConfigService) {
        this.myExternalConfigService = myExternalConfigService;
    }
}
1.2.7:定义服务消费者java config类
// Java config
@Configuration
// 开启外部化配置
@EnableDubboConfig
// externalconsumerconfig.externalConsumerConfigInfo.properties
@PropertySource("externalconsumerconfig/externalConsumerConfigInfo.properties")
// 扫描MyExternalConfigConusmerService.class所在的包以及其子包
@DubboComponentScan(basePackageClasses = { MyExternalConfigConusmerService.class })
@ComponentScan(basePackageClasses = { MyExternalConfigConusmerService.class })
public class MyExternalConfigConsumerConfiguration {

}

@ComponentScan是使用spring的扫描功能扫描成为spring bean,@DubboComponentScan是使用dubbo的扫描功能扫描@Reference注解。

1.2.8:定义服务消费者main类
public class MyConsumerWithExternalConfigMain {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(MyExternalConfigConsumerConfiguration.class);
        System.out.println("ApplicationConfig is: " + ac.getBean(ApplicationConfig.class));
        System.out.println("RegistryConfig is: " + ac.getBean(RegistryConfig.class));
        MyExternalConfigConusmerService myExternalConfigConusmerService
                = ac.getBean("myExternalConfigConusmerService", MyExternalConfigConusmerService.class);
        MyExternalConfigService myExternalConfigService = myExternalConfigConusmerService.returnClassProviderService();
        System.out.println(myExternalConfigService.sayHi("why me!!!"));
    }
}

运行,看到如下输出即为成功:

...
external say hi: why me!!!
...

从上面的例子中我们可以看到,dubbo的外部化配置其实就是约定了某些规则配置到指定的配置类,完整的的映射关系如下图所示:

当dubbo扫描到使用了@EnableDubboConfig注解时,会解析@PropertySource注解指定的配置文件,解析外部化配置信息,按照上图的规则,按需装配配置bean,可以看到,外部化配置相比于xml配置,注解配置等更加简单,也更加灵活,不需要硬编码。解析来我们一起看下多dubbo配置绑定。

1.3:多dubbo配置绑定

考虑这样的场景,服务提供者在启动的时候可能需要将自己的信息注册到多个注册中信息,比如同时注册到zk,和nacos中,在xml配置时我们可以如下的配置方式:


    
    

但是对于单dubbo配置bean绑定,这种需求明显是不支持的,为了满足这种需求,2.5.8版本中dubbo就提供了多dubbo配置bean绑定,当mutiple设置为true时生效,但是最开始支持时属性名称前缀是和单dubbo配置bean绑定是一样的,但是这明显不能够表达多个的语义,因此在2.5.9版本中在单dubbo配置bean绑定的属性前缀基础上增加s,关于这个问题可以参考这个issue 。此时,因为同时生成多个bean,因此就需要指定bean名称了,因此多dubbo配置绑定的格式是dubbo.applications.${self_define_beanname}.${property_name},(以com.alibaba.dubbo.config.ApplicationConfig为例:

# multiple Bean definition
dubbo.applications.applicationBean.name = dubbo-demo-application
dubbo.applications.applicationBean2.name = dubbo-demo-application2
dubbo.applications.applicationBean3.name = dubbo-demo-application3

以上就会创建三个ApplicationConfig的spring bean,bean名称分别为applicationBean,applicationBean2,applicationBean3,name属性的值分别是dubbo-demo-application,dubbo-demo-application2,dubbo-demo-application3。为了加深理解,我们还是来一起看一个实际的例子。

源码 。

1.3.1:定义服务接口和实现类
  • 服务接口
public interface MyMultipleExternalConfigService {
    String sayHi(String word);
}
  • 服务实现类
// 注意这里通过application设置要引用的application spring 配置bean是哪个,不然会报如下错误:

@Service(application = "applicationBean2") 
@Component("myMultipleExternalConfigService")
public class MyMultipleExternalConfigServiceImpl implements MyMultipleExternalConfigService {
    @Override
    public String sayHi(String word) {
        return "multiple external config say hi: " + word;
    }
}
1.3.2:定义服务提供者外部配置文件

classpath:multipleexternalconfig/multipleExternalConfigInfo.properties:

dubbo.applications.applicationBean.name = dubbo-demo-application
dubbo.applications.applicationBean2.name = dubbo-demo-application2
dubbo.applications.applicationBean3.name = dubbo-demo-application3
dubbo.registries.zkBean1.address=zookeeper://127.0.0.1:2181
dubbo.registries.zkBean2.address=zookeeper://127.0.0.1:2181
dubbo.protocols.myProtocolBean.name=dubbo
dubbo.services.myServiceBean.interface=dongshi.daddy.service.multipleexternalconfig.MyMultipleExternalConfigService
dubbo.services.myServiceBean.ref=myMultipleExternalConfigService
dubbo.providers.myProviderBean.host=127.0.0.1
1.3.3:定义服务提供者java config类
@Configurable
@PropertySource("classpath:multipleexternalconfig/multipleExternalConfigInfo.properties")
@EnableDubboConfig(multiple = true)
@DubboComponentScan(basePackages = { "dongshi.daddy.service.multipleexternalconfig" })
public class MyMultipleExternalConfigConfiguration {}
1.3.4:定义服务提供者main类
public class ProviderWithMultipleExternlConfigMain {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MyMultipleExternalConfigConfiguration.class);
        context.refresh();
        
        System.out.println(context.getBean("zkBean1", RegistryConfig.class));
        System.out.println(context.getBean("zkBean2", RegistryConfig.class));
        // 主进程不退出
        System.in.read();
    }
}

运行,在zk中查到如下信息则说明成功了:

[zk: localhost:2181(CONNECTED) 4] ls /dubbo/dongshi.daddy.service.multipleexternalconfig.MyMultipleExternalConfigService/providers
[dubbo%3A%2F%2F192.168.2.107%3A20880%2Fdongshi.daddy.service.multipleexternalconfig.MyMultipleExternalConfigService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-application%26bean.name%3Dproviders%3Adubbo%3Adongshi.daddy.service.multipleexternalconfig.MyMultipleExternalConfigService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Ddongshi.daddy.service.multipleexternalconfig.MyMultipleExternalConfigService%26methods%3DsayHi%26pid%3D7036%26side%3Dprovider%26timestamp%3D1640432587208]
1.3.5:定义服务消费者外部配置文件

multipleexternalconsumerconfig/multipleExternalConsumerConfigInfo.properties:

dubbo.applications.myConsumerApplication.name=dongshidaddy-consumer
dubbo.applications.myConsumerApplication.owner=dongshidaddy
dubbo.registries.myRegistryBean1.address=zookeeper://127.0.0.1:2181
dubbo.registries.myRegistryBean2.address=zookeeper://127.0.0.1:2181
1.3.6:定义服务消费者引用服务提供者的类
@Component("myMultipleExternalConfigConusmerService")
public class MyMultipleExternalConfigConusmerService {
    @Reference(interfaceClass = MyMultipleExternalConfigService.class)
    private MyMultipleExternalConfigService myMultipleExternalConfigService;

    public MyMultipleExternalConfigService returnClassProviderService() {
        return myMultipleExternalConfigService;
    }

    public MyMultipleExternalConfigService getMyMultipleExternalConfigService() {
        return myMultipleExternalConfigService;
    }

    public void setMyMultipleExternalConfigService(MyMultipleExternalConfigService myMultipleExternalConfigService) {
        this.myMultipleExternalConfigService = myMultipleExternalConfigService;
    }
}
1.3.7:定义服务消费者Java config类
// Java config
@Configuration
// 开启外部化配置
@EnableDubboConfig
// externalconsumerconfig.externalConsumerConfigInfo.properties
@PropertySource("multipleexternalconsumerconfig/multipleExternalConsumerConfigInfo.properties")
// 扫描MyExternalConfigConusmerService.class所在的包以及其子包
@DubboComponentScan(basePackageClasses = { MyMultipleExternalConfigConusmerService.class })
@ComponentScan(basePackageClasses = { MyMultipleExternalConfigConusmerService.class })
public class MyMultipleExternalConfigConsumerConfiguration {
}
1.3.8:定义服务消费者main类
public class MyMultipleConsumerWithExternalConfigMain {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext();
        ac.register(MyMultipleExternalConfigConsumerConfiguration.class);
        ac.refresh();
        MyMultipleExternalConfigConusmerService myMultipleExternalConfigConusmerService
                = ac.getBean("myMultipleExternalConfigConusmerService", MyMultipleExternalConfigConusmerService.class);
        MyMultipleExternalConfigService myMultipleExternalConfigService = myMultipleExternalConfigConusmerService.returnClassProviderService();
        System.out.println(myMultipleExternalConfigService.sayHi("why me!!!"));
    }
}

运行,看到如下输出则说明成功了:

...
multiple external config say hi: why me!!!
...

截止到这里,我们分析了dubbo约定的单dubbo配置bean绑定,和多dubbo配置bean绑定,这种约定的方式,支持绑定的外部属性前缀都是固定的,不可定制,为了为了提高弹性,dubbo提供了@EnableDubboConfigBinding注解和EnableDubboConfigBindings注解,为了完整性,我们也一起来看下。

1.4:@EnableDubboConfigBinding

源码 。

为了能够实现对外部绑定属性前缀的定制,dubbo定义了@EnableDubboConfigBinding注解,源码如下:

// 用在类上,用在注解上
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
// 保留到运行时
@Retention(RetentionPolicy.RUNTIME)
@documented
// 通过DubboConfigBindingRegistrar引入bean定义从而引入spring bean
@import(DubboConfigBindingRegistrar.class)
public @interface EnableDubboConfigBinding {
    // 用于绑定到一个AbstractConfig子类的自定义属性前缀
    String prefix();
    // 要绑定的AbstractConfig的子类的class对象
    Class type();
    // 自定义属性前缀是否绑定到多个配置bean
    boolean multiple() default false;
}

一个@EnableDubboConfigBinding可以自定义一个属性前缀到一个AbstractConfig的子类,但是我们可能需要自定义多个属性前缀到多个AbstractConfig子类,在jdk中提供了@Repeatable 来允许一个注解重复定义到类上,但是从源码可以看到并没有继承该注解,而之所以没有使用的原因是要考虑到比较老的jdk版本的兼容性,@Repeatable 注解是jdk8才定义的,源码如下:

@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class value();
}

从@since 1.8可以看出来。为此,dubbo定义了@EnableDubboConfigBindings,源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {
    EnableDubboConfigBinding[] value();
}

可以看到定义了EnableDubboConfigBinding数组来支持定义多个,下面我们来看一个完整的例子。

1.4.1:定义服务接口和实现类
  • 服务接口
public interface MyEnableDubboBindingService {
    String sayHi(String word);
}
  • 服务实现类
@Service
public class MyEnableDubboBindingServiceImpl implements MyEnableDubboBindingService {
    @Override
    public String sayHi(String word) {
        return "@EnableDubboBinding say hi: " + word;
    }
}
1.4.2:定义服务提供者外部配置文件

classpath:enabledubbobinding/customExternalConfigInfo.properties:

myapplication.placeholder=mydubbo.myapplication.

mydubbo.myapplication.name=dongshidaddy-provider
mydubbo.myapplication.owner=dongshidaddy
mydubbo.mymyregistry.address=zookeeper://127.0.0.1:2181
mydubbo.myprotocol.name=dubbo
mydubbo.myprotocol.port=20827

可以看到我们使用的是自定义的dubbo配置绑定属性前缀,后续会在程序中定义对应的AbstractConfig的具体子类。

1.4.3:定义服务提供者Java config类
@Configurable
@EnableDubboConfig
// prefix最后带不带".",效果都是一样的,这里只是为了示意,有的带了,有的没带,另外还可以使用占位符的方式来指定
@EnableDubboConfigBindings({
        // 使用占位符方式来指定
        @EnableDubboConfigBinding(prefix = "${myapplication.placeholder}", type = ApplicationConfig.class),
        // 直接指定,不带"."
        @EnableDubboConfigBinding(prefix = "mydubbo.mymyregistry", type = RegistryConfig.class),
        // 直接指定,带"."
        @EnableDubboConfigBinding(prefix = "mydubbo.myprotocol.", type = ProtocolConfig.class)
})
@DubboComponentScan(basePackageClasses = MyEnableDubboBindingService.class)
@PropertySource("classpath:enabledubbobinding/customExternalConfigInfo.properties")
public class MyEnableDubboConfigBindingConfiguration {
}
1.4.4:定义服务提供者main类
public class MyEnableDubboConfigBindingMain {

    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.register(MyEnableDubboConfigBindingConfiguration.class);
        ac.refresh();

        System.in.read();
    }
}

启动成功后,在zk中看到如下信息则说明成功了:

[zk: localhost:2181(CONNECTED) 4] ls /dubbo/dongshi.daddy.service.enabledubbobinding.MyEnableDubboBindingService/providers
[dubbo%3A%2F%2F192.168.2.107%3A20827%2Fdongshi.daddy.service.enabledubbobinding.MyEnableDubboBindingService%3Fanyhost%3Dtrue%26application%3Ddongshidaddy-provider%26bean.name%3Dproviders%3Adubbo%3Adongshi.daddy.service.enabledubbobinding.MyEnableDubboBindingService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Ddongshi.daddy.service.enabledubbobinding.MyEnableDubboBindingService%26methods%3DsayHi%26owner%3Ddongshidaddy%26pid%3D2588%26side%3Dprovider%26timestamp%3D1640517528685]
1.4.5:定义服务消费者外部配置文件

multipleexternalconsumerconfig/multipleExternalConsumerConfigInfo.properties:

dubbo.applications.myConsumerApplication.name=dongshidaddy-consumer
dubbo.applications.myConsumerApplication.owner=dongshidaddy
dubbo.registries.myRegistryBean1.address=zookeeper://127.0.0.1:2181
dubbo.registries.myRegistryBean2.address=zookeeper://127.0.0.1:2181
1.4.6:定义服务消费者引用服务提供者的类
@Component("myEnableDubboConfigBindingService")
public class MyEnableDubboConfigBindingService {

    @Reference
    private MyEnableDubboBindingService myEnableDubboBindingService;

    public MyEnableDubboBindingService returnProviderService() {
        return myEnableDubboBindingService;
    }
}
1.4.7:定义服务消费者引main类
public class MyEnableDubboConfigBindingMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext();
        ac.register(MyEnableDubboConfigBindingConsumerConfiguration.class);
        ac.refresh();

        MyEnableDubboBindingService myEnableDubboConfigBindingService
                = ac.getBean("myEnableDubboConfigBindingService", MyEnableDubboConfigBindingService.class).returnProviderService();
        System.out.println(myEnableDubboConfigBindingService.sayHi("younger sister in USA"));
    }
}

运行,看到如下输出则说明成功了:

... 
@EnableDubboBinding say hi: younger sister in USA
...
写在后面

参考文章:

Dubbo 新编程模型之外部化配置 。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/688547.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号