- disconf是什么:
disconf根据英文的字面意思可以理解成分布式的配置中心(distributed configuration),无论大大小小的系统与配置中心的联系还是比较紧密的,对于业务中会用到,但不会经常变化,却又不适合在代码中耦合的变量,适合抽离到配置中心,如db、cache、还有业务中用到的一些变量配置都离不开配置中心的支持,配置中心的实现又分为好多种,有直接简单快捷的基于zookeeper实现,毕竟zookeeper配置管理也是它具备的一个基础能力,有基于consul的,大公司还有自研的等等,实现方案也是比较多,但是目标比较一致,要求配置中心高可用、数据更新实时性、承载更多的client请求的要求,有了如上的业务场景,从众多的方案中选用满足自己场景的方案即可! - 如何用:
1、安装disconf-web、zookeeper集群,不一一赘述了,详细可以参考官方文档:https://disconf.readthedocs.io/zh_CN/latest/install/index.html
2、增加pom引入:<dependency> <groupId>com.baidu.disconf</groupId> <artifactId>disconf-client</artifactId> <version>2.6.36</version> </dependency
3、修改项目配置:
3.1、增加支持disconf的配置<bean id="disconfMgrBean" class="com.baidu.disconf.client.DisconfMgrBean" destroy-method="destroy"> <property name="scanPackage" value="com.example.disconf.demo"/> </bean> <bean id="disconfMgrBean2" class="com.baidu.disconf.client.DisconfMgrBeanSecond" init-method="init" destroy-method="destroy"> </bean>
3.2、开启对cglib aop的支持<aop:aspectj-autoproxy proxy-target-class="true"/>
4、业务使用
4.1、注解方式@DisconfFile-类级别的注解,告诉这个类使用这个配置文件,这个配置文件就是通过disconf-web上传的文件 @DisconfFileItem-方法级别的注解,注解在某一个getXX方法上,告诉这个GET方法使用配置文件内容的哪一个key(配置文件的规范是key=value) @DisconfItem-方法级别的注解,注解在某一个getXX方法上,告诉这个GET方法使用哪个配置项(这个配置项是在disconf-web上新建的一个key-value的值),注意:这个配置与分布式配置文件没有任何关系!!! 以上注解的使用示例如下:
@Service @Scope("singleton") @DisconfFile(filename = "redis.properties") public class JedisConfig { // 代表连接地址 private String host; // 代表连接port private int port; /** * 地址, 分布式文件配置 * * @return */ @DisconfFileItem(name = "redis.host", associateField = "host") public String getHost() { return host; } public void setHost(String host) { this.host = host; } /** * 端口, 分布式文件配置 * * @return */ @DisconfFileItem(name = "redis.port", associateField = "port") public int getPort() { return port; } public void setPort(int port) { this.port = port; } }
@Service public class Coefficients { public static final String key = "discountRate"; @Value(value = "2.0d") private Double discount; /** * 折扣率,分布式配置 * * @return */ @DisconfItem(key = key) public Double getDiscount() { return discount; } public void setDiscount(Double discount) { this.discount = discount; } }
4.2、非注解方式
4.2.1、代码中使用方式与原生spring使用配置的方式相同,变量名使用@Value注解
4.2.2、增加两个配置(支持各种文件格式的注入,仅properties文件更新时会对bean的属性更新,其他类型文件需要利用更新回调的配置):<bean id="configproperties_disconf" class="com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean"> <property name="locations"> <list> <value>classpath:/autoconfig.properties</value> <value>classpath:/autoconfig2.properties</value> <value>classpath:/myserver_slave.properties</value> <value>classpath:/testJson.json</value> <value>classpath:/testXml2.xml</value> <value>myserver.properties</value> </list> </property> </bean> <bean id="propertyConfigurer" class="com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer"> <property name="propertiesArray"> <list> <ref bean="configproperties_disconf"/> </list> </property> </bean>
4.3、配置更新
默认情况下(如果是基于xml的配置具体要看配置:https://disconf.readthedocs.io/zh_CN/latest/quick/src/TutorialSummary.html#id1),配置文件或者配置项的变更会,直接刷新对应的bean属性,但是对于已经引用bean的旧属性生成的对象,需要增加事件回调,进行更新该对象,简单的例子:通常情况下为了提升效率,在服务初始化或者首次调用时会初始化数据库连接池或者缓存连接池,在服务运行过程中,修改了db或者cache的地址,那么bean中采用@Value、@DisconfFileItem、@DisconfItem注解的方法或属性当调用getXX方法时会得到最新值,对于引用该Bean的属性已经创建的connection,则需要重新去创建,这里介绍的就是重新创建引用对象的回调事件。
4.3.1、继承IdisconfUpdate接口,实现reload方法
4.3.2、增加@DisconfUpdateService注解,告诉这个bean当某个配置类变化或者某些Key变化时候,执行该bean的重新加载方法,示例如下:@Service @Scope("singleton") @DisconfFile(filename = "redis.properties") @DisconfUpdateService(classes = {JedisConfig.class}) public class JedisConfig implements IDisconfUpdate { protected static final Logger LOGGER = LoggerFactory.getLogger(JedisConfig.class); // 代表连接地址 private String host; // 代表连接port private int port; /** * 地址, 分布式文件配置 * * @return */ @DisconfFileItem(name = "redis.host", associateField = "host") public String getHost() { return host; } public void setHost(String host) { this.host = host; } /** * 端口, 分布式文件配置 * * @return */ @DisconfFileItem(name = "redis.port", associateField = "port") public int getPort() { return port; } public void setPort(int port) { this.port = port; } @Override public void reload() throws Exception { //配置变化的时候,创新创建某个jedisclient JedisUtils.reCreateConfig(host, port); } }
- 架构和原理讲解:
1、引用官方的一个架构设计图:
2、原理讲解:
2.1、用户把文件上传至disconf-web,或者在上面新建分布式配置项
2.2、服务启动时,通过扫描,获取哪些类、哪些属性需要获取分布式的配置,根据注解知道分布式配置项和配置文件是哪些
2.3、服务启动后,会通过http方式请求disconf-web,下载注解过的class所需要的配置文件到本地
2.4、文件下载到local以后,把注解中需要用到的文件、字段属性提取出来与注解的bean和属性建立对应关系,保存对应关系在内存中
2.5、根据文件或配置项增加zookeeper的监听,数据节点发生变更,刷新2.4中的提到的内存仓库
2.6、结合AOP的原理,当用户调用getXX时候,从内存中获取对应的值,实现配置的动态装载
2.7、非注解的方式,与AOP无关,通过重写PlaceholderResolvingStringValueResolver,对配置数据做重新构造,交由原来的spring处理,也就是为什么我们使用@Value取数据 - 优缺点总结:
优点:轻量级部署,对系统的侵入也较小,可以做到配置数据的实时生效,友好的兼容了spring框架,满足基本的配置应用场景
缺点:采用拉取的方式获取数据,当ZK的watch监测到节点数据变化,会做数据拉取,在客户端较多的情况下,容易对disconf-web形成并发的压力。
转载于:https://my.oschina.net/13426421702/blog/3046966