`
Technoboy
  • 浏览: 156704 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Disconf原理解析

阅读更多
1. 介绍
  Disconf是一套完整的基于zookeeper的分布式配置统一解决方案。它支持配置(配置项+配置文件)的分布式化管理。

2. 安装
  依赖Mysql, Tomcat, Nginx, Zookeeeper, Redis。
  路径配置:
  将你的配置文件放到此地址目录下(以下地址可自行设定):
/home/work/dsp/disconf-rd/online-resources

    如果不确定如何配置,可以拷贝/disconf-web/profile/rd/目录下的文件,拷贝过去后,依次修改配置文件内容即可。配置文件包括:
  • jdbc-mysql.properties (数据库配置)
  • redis-config.properties (Redis配置,主要用于web登录使用)
  • zoo.properties (Zookeeper配置)
  • application.properties (应用配置)

  *注意,记得执行将application-demo.properties复制成application.properties。
  *注意,即使只有一个redis,也应该配置两个redis client,否则将造成内部错误。
 
  设置War包将要被部署的地址(以下地址可自行设定):
/home/work/dsp/disconf-rd/war

  构建
ONLINE_CONFIG_PATH=/home/work/dsp/disconf-rd/online-resources
WAR_ROOT_PATH=/home/work/dsp/disconf-rd/war
export ONLINE_CONFIG_PATH
export WAR_ROOT_PATH
cd disconf-web
sh deploy/deploy.sh

  这样会在 /home/work/dsp/disconf-rd/war 生成以下结果:
-disconf-web.war
-html
-META-INF
-WEB-INF

  Mysql:
    参考disconf-web/sql的readme.md进行sql脚本初始化。
  Tomcat:
    修改server.xml文件,在Host结点下设定Context:
<Context path="" docBase="/home/work/dsp/disconf-rd/war"></Context>
并设置端口为 8015(端口号与Nginx的upstream模块中server的端口号匹配就行),启动Tomcat,即可。
   Nginx:
    修改 nginx.conf
upstream disconf {
    server 127.0.0.1:8015;
}

server {

    listen   8081;
    server_name disconf.com;
    access_log /home/work/var/logs/disconf/access.log;
    error_log /home/work/var/logs/disconf/error.log;

    location / {
        root /home/work/dsp/disconf-rd/war/html;
        if ($query_string) {
            expires max;
        }
    }

    location ~ ^/(api|export) {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://disconf;
    }
}

  关于host:
    这里的 host 设置成 disconf.com (可以自定义),但它必须与application.properties 里的domain一样。然后浏览器的访问域名也是这个。
  通过Nginx(处理静态请求) + Tomcat(处理动态请求)达到处理请求的逻辑。
  在浏览器输入:http://localhost:8081后表明安装成功,默认登录用户名admin/admin。


3. 架构设计

  Disconf通过disconf-web管理配置信息,然后将配置的key在Zookeeper上建立节点,disconf-client启动后拉取自身需要的配置信息并监听Zookeeper的节点。在web上更新配置信息会触发zk节点状态的变动,client可以实时感知到变化,然后从web上拉取最新配置信息。

4. 使用-基于注解
  由于最新版Disconf(2.6.36)只支持Spring项目集成,非spring项目暂时无法使用。添加Maven依赖:
<dependency>
			<groupId>com.baidu.disconf</groupId>
			<artifactId>disconf-client</artifactId>
			<version>2.6.36</version>
		</dependency>

  在需要进行配置的类通过DisconfFile注解指名配置从哪个文件获取,在get方法通过DisconfFileItem注解的name属性指定文件中的key,associateField指类的属性,即当前值设置到当前类的哪个属性中。
 
@Service
@DisconfFile(filename = "redis.properties")
public class JedisConfig {

	private String host;
	
	private int port;

	@DisconfFileItem(name = "redis.host", associateField = "host")
	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	@DisconfFileItem(name = "redis.port", associateField = "port")
	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}
}

@Service
public class SimpleRedisService implements InitializingBean, DisposableBean{
	
	protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisService.class);

    private Jedis jedis = null;

    @Autowired
    private JedisConfig jedisConfig;

	@Override
	public void destroy() throws Exception {
		if (jedis != null) {
            jedis.disconnect();
        }
	}
	
    public String getKey(String key) {
        if (jedis != null) {
            return jedis.get(key);
        }
        return null;
    }

    public void changeJedis() {
        LOGGER.info("start to change jedis hosts to: " + jedisConfig.getHost() + " : " + jedisConfig.getPort());
        jedis = JedisUtil.createJedis(jedisConfig.getHost(), jedisConfig.getPort());
        LOGGER.info("change ok.");
    }

	@Override
	public void afterPropertiesSet() throws Exception {
		jedis = JedisUtil.createJedis(jedisConfig.getHost(), jedisConfig.getPort());
	}
}

  通过DisconfUpdateService注解指定哪些配置更新时,进行配置的更新。注解了DisconfUpdateService的类同时要实现IDisconfUpdate方法。
@Service
@DisconfUpdateService(classes = {JedisConfig.class})
public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate{

	@Autowired
	private SimpleRedisService simpleRedisService;
	
	@Override
	public void reload() throws Exception {
		simpleRedisService.changeJedis();
	}
}

  添加配置spring的扫描类:
<bean id="disconfMgrBean" class="com.baidu.disconf.client.DisconfMgrBean" destroy-method="destroy">
		<property name="scanPackage" value="com.dfire.missile" />
	</bean>
<bean id="disconfMgrBean2" class="com.baidu.disconf.client.DisconfMgrBeanSecond" init-method="init" 
		destroy-method="destroy">
</bean>

  最后,在classpath下添加disconf.properties文件:
#为上面nginx地址
disconf.conf_server_host=localhost:8081
#disconf需要指定应用的app名
disconf.app=test
#版本号,推荐x_x_x_x形式
disconf.version=1_0_0_0
#是否开启从远程仓库获取配置
disconf.enable.remote.conf=true
#指定获取环境,rd,qa,local,online四个值
disconf.env=rd
#忽略的分布式配置,用空格分隔
disconf.ignore=
#调试模式。调试模式下,ZK超时或断开连接后不会重新连接(常用于client单步debug)。非调试模式下,ZK超时或断开连接会自动重新连接。
disconf.debug=false
#获取远程配置 重试次数,默认是3次
disconf.conf_server_url_retry_times=1
#获取远程配置 重试时休眠时间,默认是2秒
disconf.conf_server_url_retry_sleep_seconds=1
#用户定义的下载文件夹, 远程文件下载后会放在这里。注意,此文件夹必须有有权限,否则无法下载到这里,默认./disconf/download
disconf.user_define_download_dir=/disconf/download
#下载的文件会被迁移到classpath根路径下,强烈建议将此选项置为 true(默认是true)	
disconf.enable_local_download_dir_in_class_path=false

  假如,先前我们在web上创建了redis.properties文件,且设置了redis.host,redis.port值,当client启动后,我们就可以在/disconf/download目录下,看到redis.properties文件已经下载下来了。如果我们在web上进行redis.host,redis.port修改,可以看到如下log:
LOGGER.info("start to change jedis hosts to: " + ***+ " : " + ***);
LOGGER.info("change ok.");

  以上,我们通过了@DisconfFile和@DisconfFileItem组合,进行了文件配置获取。还可以通过@DisconfItem进行KV值的配置获取。DisconfItem的key为web端设置的"配置项目"
@Service
public class Host {

	private int size;
	
	private int threshold;
	
	@DisconfItem(key = "missile_gateway_msg_threshold", associateField = "threshold")
	public int getThreshold() {
		return threshold;
	}

	public void setThreshold(int threshold) {
		this.threshold = threshold;
	}

	@DisconfItem(key = "missile_gateway_msg_size", associateField = "size")
	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}
}

  然后再配置@DisconfUpdateService并实现IDisconfUpdate接口即可:
@Service
@DisconfUpdateService
public class HostCallcack implements IDisconfUpdatePipeline, IDisconfUpdate{
	
	protected static final Logger LOGGER = LoggerFactory.getLogger(HostCallcack.class);
	
	@Autowired
	private Host host;

	@Override
	public void reloadDisconfFile(String key, String filePath) throws Exception {
		LOGGER.info("reload file key: " + key + " , filePath : " + filePath);
		
	}

	@Override
	public void reloadDisconfItem(String key, Object content) throws Exception {
		LOGGER.info("reload item key: " + key + " , content : " + content);
		LOGGER.info("threshold : " + host.getThreshold());
		LOGGER.info("size : " + host.getSize());
	}

	public void reload() throws Exception {
		LOGGER.info("threshold : " + host.getThreshold());
		LOGGER.info("size : " + host.getSize());
	}

}

  这一次,我们还实现了IDisconfUpdatePipeline接口,它和IDisconfUpdate的区别在于,前者可以同时监听到DisconfFile和DisconfItem的变化。

5. 使用-基于xml
  xml方式除了需要添加上面的spring bean外,还需要指定哪些配置文件需要更新,哪些对应的bean需要从这些文件中加载数据:
<!--需要拉取更新的文件 -->
<bean id="configproperties_disconf"
 class="com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean">
 <property name="locations">
 <list>
 <value>file:${disconf.user_define_download_dir}/data.properties</value> 
 <value>file:${disconf.user_define_download_dir}/dubbo.properties</value>
 <value>file:${disconf.user_define_download_dir}/dynamic.properties</value>
 </list>
 </property>
</bean>
<!--内容有变动,重新reload -->
<bean id="propertyConfigurer"
 class="com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer">
 <property name="ignoreResourceNotFound" value="true"/>
 <property name="ignoreUnresolvablePlaceholders" value="true"/>
 <property name="propertiesArray">
 <list>
 <ref bean="configproperties_disconf"/>
 </list>
 </property>
</bean>
<!--配置的bean -->
<bean id="autoService" class="com.example.disconf.demo.service.AutoService">
    <property name="auto" value="${auto=100}"/>
</bean>

<bean id="autoService2" class="com.example.disconf.demo.service.AutoService2">
    <property name="auto2" value="${auto2=100}"/>
</bean>


6. 原理-client

  我们主要关注第三块-系统正常运行时请求配置数据:对配置数据进行AOP拦截。这里可以保证对于数据总是从仓库获取值,且对于更新数据也能立即生效。com.baidu.disconf.client.store.aspect.DisconfAspectJ为AOP类,分别对File和Item进行了切面:
@Around("anyPublicMethod() && @annotation(disconfFileItem)")
    public Object decideAccess(ProceedingJoinPoint pjp, DisconfFileItem disconfFileItem) throws Throwable {

        if (DisClientConfig.getInstance().ENABLE_DISCONF) {

            MethodSignature ms = (MethodSignature) pjp.getSignature();
            Method method = ms.getMethod();

            //
            // 文件名
            //
            Class<?> cls = method.getDeclaringClass();
            DisconfFile disconfFile = cls.getAnnotation(DisconfFile.class);

            //
            // Field名
            //
            Field field = MethodUtils.getFieldFromMethod(method, cls.getDeclaredFields(), DisConfigTypeEnum.FILE);
            if (field != null) {

                //
                // 请求仓库配置数据
                //
                DisconfStoreProcessor disconfStoreProcessor =
                        DisconfStoreProcessorFactory.getDisconfStoreFileProcessor();
                Object ret = disconfStoreProcessor.getConfig(disconfFile.filename(), disconfFileItem.name());
                if (ret != null) {
                    LOGGER.debug("using disconf store value: " + disconfFile.filename() + " ("
                            + disconfFileItem.name() +
                            " , " + ret + ")");
                    return ret;
                }
            }
        }

        Object rtnOb;

        try {
            // 返回原值
            rtnOb = pjp.proceed();
        } catch (Throwable t) {
            LOGGER.info(t.getMessage());
            throw t;
        }

        return rtnOb;
    }

    /**
     * 获取配置项数据, 只有开启disconf远程才会进行切面
     *
     * @throws Throwable
     */
    @Around("anyPublicMethod() && @annotation(disconfItem)")
    public Object decideAccess(ProceedingJoinPoint pjp, DisconfItem disconfItem) throws Throwable {

        if (DisClientConfig.getInstance().ENABLE_DISCONF) {
            //
            // 请求仓库配置数据
            //
            DisconfStoreProcessor disconfStoreProcessor = DisconfStoreProcessorFactory.getDisconfStoreItemProcessor();
            Object ret = disconfStoreProcessor.getConfig(null, disconfItem.key());
            if (ret != null) {
                LOGGER.debug("using disconf store value: (" + disconfItem.key() + " , " + ret + ")");
                return ret;
            }
        }

        Object rtnOb;

        try {
            // 返回原值
            rtnOb = pjp.proceed();
        } catch (Throwable t) {
            LOGGER.info(t.getMessage());
            throw t;
        }

        return rtnOb;
    }


7. 原理-web

  web主要用于管理配置项,当新建配置项后,会保存到DB的config及config-history表,并邮件通知app表对应的邮件地址。
对更新的配置项,会更新config表并新增记录到config-history表,邮件通知,同时添加zk节点(/disconf/app_version_env/file(或item)/配置项),当client端更新配置后,会在此节点下写入client标识,所以就有了web端的“实例列表”。
  client启动后会从web拉取最新的配置文件信息,并监听相应的zk节点,当有数据变化时,zk会通知client,然后client重新从web拉取最新数据。
  web的另外一个功能是配置项检查,每30分钟将DB config表中配置值与zk相比,发现不一致会邮件通知。

8. DB-table
  Disconf只有如下7张表,功能描述如下:
  • app: app表,配置以app为核心。
  • config: 配置表。
  • config_history: 配置记录表,每更新每新增。
  • env: 环境表。
  • role: 角色表,用户有哪些角色。
  • role_resource: 角色权限对应的功能表。
  • user: 用户表,登录使用。


9. Disconf VS Diamond VS Apollo
Diamond Disconf Apollo
数据持久性 存储在mysql上 mysql mysql
推拉模型 拉模型,http长轮询基于ZK,实时 http长轮询,1s
配置读写 支持实例对配置读写
容灾 多级容灾模式多级 多级
配置数据模型 只支持KV结构的数据 文件和KV KV
是否支持灰度
客户端配置信息监控

  解释下,Apollo的长轮询为啥是1s。首先Apollo为了减少对外中间件的依赖,将消息消费中间件改为依赖DB的扫表操作。当用户通过Portal更新配置时,向ReleaseMessage表插一条记录,其Config Service有个线程会每秒扫表,检查到有数据后,会通知注册的监听服务即client,然后进行拉取更新操作。

10. 注意事项
  • 注解是放在get方法之上的,对于”call self”的方法调用,AOP无法拦截得到(此时读取对应bean属性的默认值),这样就无法统一处理这些配置。一旦出现这种情况,“非一致性读问题”就会产生。
  • web在zk连接异常的情况下,对配置项的修改也会成功。这将导致client端无法实时获取最新数据以及zk节点数据与db数据不一致问题。当修改配置项时,发现web端提示zk异常情况,请多次执行相同值修改操作直到无异常提示。对于后者,web端提供了定时任务每30m检测两者间的数据一致性问题。
  • 大小: 99.7 KB
  • 大小: 37.3 KB
  • 大小: 175.7 KB
  • 大小: 10.6 KB
  • 大小: 16.3 KB
分享到:
评论

相关推荐

    disconf-web简化可部署版

    3. **压缩包文件解析**: - **newconfig_file.html, modifyFile.html**:这些文件用于处理配置文件的新增和修改操作,提供了用户交互界面。 - **main.html, newconfig_item.html, modifyItem.html**:主要用于配置...

    Disconf配置war包

    Disconf(分布式配置中心)是百度开源的一款用于解决分布式系统配置管理问题的工具,它可以集中化管理应用的配置,使得在分布式环境下配置的修改和更新变得更加便捷。在本压缩包中,包含了`disconf-web.war`文件,这...

    Disconf 分布式配置使用教程

    Disconf 分布式配置使用教程 Disconf 是一款分布式配置管理系统,旨在帮助开发者更方便地管理和维护项目中的配置文件。在本教程中,我们将详细介绍 Disconf 的使用方法和配置项。 1. Disconf 客户端录入/修改/删除...

    disconf-master.zip

    本篇文章将详细介绍Disconf的核心组件、工作原理以及如何在实际项目中进行应用。 一、Disconf核心组件 1. disconf-core:这是Disconf的基础库,包含了配置管理的基本功能,如配置的读取、监听、更新等。它提供了...

    disconf-web-2.6.33简化版

    1.按照disconf-web.war/sql/readme.md执行SQL脚本创建库表和基础数据。 2.修改war包下的配置文件,具体目录是disconf-web.war\WEB-INF\classes\ jdbc-mysql.properties (数据库配置) redis-config.properties ...

    docker快速构建disconf镜像

    dd676e1ecbee zookeeper:3.3.6 "/docker-entrypoin..." About an hour ago Up About an hour 0.0.0.0:2181-&gt;2181/tcp, 0.0.0.0:2888-&gt;2888/tcp, 0.0.0.0:3888-&gt;3888/tcp dockerdisconfmaster_disconf_zookeeper_1 ...

    disconf-demo

    【标题】"disconf-demo" 是一个基于Spring Boot框架的示例程序,它展示了如何集成和使用disconf(分布式配置中心)这一工具。这个项目旨在帮助开发者了解如何在Spring Boot应用中实现分布式配置管理,从而实现配置的...

    disconf-web部署指南_V1.3

    《disconf-web部署指南_V1.3》是针对disconf在Web环境下的部署与配置的一份详尽指导文档。Disconf(分布式配置中心)是一款开源的、轻量级的、适用于Java环境的分布式配置管理工具,它能有效地帮助开发者解决在...

    apollo和disconf对比

    本文将详细探讨两个常用的配置中心——Apollo和Disconf,并进行深入的对比分析。 Apollo是由携程开源的一款分布式配置中心,它提供了统一的配置管理平台,支持实时推送、多环境、多数据中心等特性。Apollo的设计...

    分布式配置中心 Disconf 安装包

    分布式配置中心 Disconf 编译好的安装包, 分布式配置中心 Disconf 编译好的安装包,。

    Disconf分布式配置管理平台 v2.6.36.zip

    Disconf(Distributed Configuration System)是一款优秀的开源分布式配置管理平台,专为解决分布式系统中的配置管理问题而设计。它的核心目标是将应用的配置统一管理,实现配置的集中化、版本化、实时更新以及监控...

    disconf使用

    《Disconf——分布式配置管理详解》 在现代的大型分布式系统中,配置管理是一个至关重要的环节。Disconf,全称为 Distributed Configuration,是一个开源的、基于Java的分布式配置中心,它致力于解决分布式环境下...

    disconf 例子

    《分布式配置中心Disconf实战详解》 在现代的大型分布式系统中,统一管理和维护配置信息是必不可少的一环。Disconf,全称为 Distributed Configuration,是一款由百度开源的分布式配置中心,它能够有效地解决分布式...

    disconf小文档

    "软件详情.html"文件可能包含更深入的介绍,比如disconf与其他配置管理工具的对比,其核心组件的工作原理,以及一些最佳实践和注意事项。例如,如何处理配置冲突,如何进行性能优化,以及在大规模集群中部署和运维的...

    disconf-client-2.6.36.jar

    适用升级disconf + springcloud 高版本

    springboot集成百度disconf

    **三、Disconf工作原理** Disconf的核心工作流程包括: 1. **启动时加载**:服务启动时,Disconf客户端会从配置中心拉取当前服务所需的所有配置文件。 2. **心跳检测**:客户端定期发送心跳到Disconf服务器,确保...

    python3通过disconf的api自动进行appname的配置文件下载和配置添加

    百度的disconf确实好用,但是手工添加配置项和配置文件很烦躁,自己利用闲余时间写了该脚本,主要支持对disconf的app自动下载和添加配置,运行之前,请先在桌面上新建一个applist.txt的文本,里面填上待操作的...

    disconf-web-2.6.36 maven项目

    disconf-web项目,可直接部署到tomcat上,不依赖nginx。 修改其中 jdbc-mysql.properties (数据库配置), redis-config.properties (Redis配置),zoo.properties (Zookeeper配置), application.properties (应用...

    disconf打包后的war

    如果不想导入eclipse用maven安装,可直接下载就可以用.

    disconf-demo:disconf分布式配置demo

    分布式配置管理在现代大型互联网应用中扮演着至关重要的角色,它允许开发者在不修改代码的情况下,...通过`disconf-demo`这个示例,你可以深入学习`disconf`的工作原理和最佳实践,提升你的分布式系统设计和运维能力。

Global site tag (gtag.js) - Google Analytics