diff --git a/README.md b/README.md index 5e8adb000..c69347db6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Disconf [![Build Status](https://travis-ci.org/knightliao/disconf.svg?branch=mas Distributed Configuration Management Platform(分布式配置管理平台) -专注于各种 `分布式系统配置管理` 的`通用组件`/`通用平台`, 提供统一的`配置管理服务`。 +专注于各种「分布式系统配置管理」的「通用组件」和「通用平台」, 提供统一的「配置管理服务」 ![](http://ww3.sinaimg.cn/mw1024/60c9620fjw1esvjzny1rmj20aj061t9a.jpg) @@ -18,37 +18,21 @@ Distributed Configuration Management Platform(分布式配置管理平台) - 统一管理:提供web平台,统一管理 多个环境(RD/QA/PRODUCTION)、多个产品 的所有配置 - 核心目标:一个jar包,到处运行 -## demos +## demos && 文档 && 协作 -https://github.com/knightliao/disconf-demos-java +- demos: https://github.com/knightliao/disconf-demos-java +- wiki: https://github.com/knightliao/disconf/wiki +- 文档: http://disconf.readthedocs.io +- 协作开发: 在 master 分支上提pull request +- 提问题: https://github.com/knightliao/disconf/issues 提issue -## 开发协作 +## 版本 -请在dev上进行提交代码 - -## 项目信息 - -- CLIENT 端: - - Java: 目前唯一支持语言 - - python:打算支持 - - PHP:暂未支持 -- WEB 管理端: - - Java SpringMvc 实现,前后端分离 实现方式(基于Spring 4.1.7.RELEASE) - -### java client - -disconf.git branches and Maven version: - -- dev(develop branch): 2.6.35 -- master(stable branch):2.6.35 -- [更新日志](https://github.com/knightliao/disconf/wiki/updates) +- dev(dev branch): 2.6.36 +- master(latest && cooperate && contribute branch):2.6.36 +- stable(release && stable branch): 2.6.35 - 在Maven Central Repository里查看 [com.baidu.disconf](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.baidu.disconf%22 ) -#### Java Client Elegant Usage Preview ## - -- [注解式分布式配置使用方式](http://ww3.sinaimg.cn/mw1024/60c9620fgw1eu5lsrsixcj20ga06ygna.jpg) -- [XML配置式分布式配置方式](http://ww1.sinaimg.cn/mw1024/60c9620fgw1eu5ltt9uglj20ia0j0tbo.jpg) - ## 功能特点 ## - 支持配置(配置项+配置文件)的分布式化管理 @@ -76,85 +60,6 @@ Disconf的功能特点描述图: - 支持配置项多个项目共享,支持批量处理项目配置。 - 配置监控:平台提供自校验功能(进一步提高稳定性),可以定时校验应用系统的配置是否正确。 -## 模块架构图 ## - -![](http://ww2.sinaimg.cn/bmiddle/006oy5Ulgw1f25zc2vfwpj30nh0d6q5t.jpg) - -[查看大图](http://ww2.sinaimg.cn/mw1024/006oy5Ulgw1f25zc2vfwpj30nh0d6q5t.jpg) - -### 模块信息### - -- CLIENT: client目标是支持多语言。目前只提供了java语言客户端。 - - JAVA - - [disconf-core](https://github.com/knightliao/disconf/tree/master/disconf-core): 分布式配置基础包模块 - - [disconf-client](https://github.com/knightliao/disconf/tree/master/disconf-client): 分布式配置客户端模块, 依赖disconf-core包。 用户程序使用它作为Jar包进行分布式配置编程。 - - [disconf-tool](https://github.com/knightliao/disconf/tree/master/disconf-tool): 分布式配置工具包,依赖disconf-core包。 Disconf-tool是disconf的辅助工具类, 目前使用不多,建议不使用。 -- 管理端:disconf-web是统一的分布式配置管理平台。[disconf-web](https://github.com/knightliao/disconf/tree/master/disconf-web): 分布式配置平台服务模块, 依赖disconf-core包。采用SpringMvc+纯HTML方式(前后端分离架构)实现。用户使用它来进行日常的分布式配置管理。 - -## 用户指南 ## - -### client - -#### java client: disconf-client 使用 ### - -在您的 Maven POM 文件里加入: - - - com.baidu.disconf - disconf-client - 2.6.34 - - -### server: disconf-web 使用 ### - -部署方法请参见:[https://github.com/knightliao/disconf/tree/master/disconf-web](https://github.com/knightliao/disconf/tree/master/disconf-web) - -全新主页,高清大图: - -APP+环境+版本+ZK查询: - -![http://ww1.sinaimg.cn/mw1024/60c9620fgw1emyww39wjmj20qw0keq6m.jpg](http://ww1.sinaimg.cn/mw1024/60c9620fgw1emyww39wjmj20qw0keq6m.jpg) - -#### 其它开源的disconf-web: - -- https://github.com/comlkz/disconf-web - -### java client Tutorials ### - -#### 总体概述 - -- [TutorialSummary 功能总体概述](https://github.com/knightliao/disconf/wiki/TutorialSummary) -- 文章介绍:[分布式配置管理平台Disconf](https://github.com/knightliao/disconf/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0Disconf) - -#### 基于注解式的分布式配置(支持配置文件和配置项) - -推荐新建的项目使用disconf时使用 - -- [Tutorial 1 注解式分布式的配置文件](https://github.com/knightliao/disconf/wiki/Tutorial1) -- [Tutorial 2 注解式分布式的配置文件高级篇: 配置更新的通知](https://github.com/knightliao/disconf/wiki/Tutorial2) -- [Tutorial 3 注解式分布式的配置项](https://github.com/knightliao/disconf/wiki/Tutorial3) -- [Tutorial 4 注解式分布式静态配置文件和静态配置项](https://github.com/knightliao/disconf/wiki/Tutorial4) - -注:将配置文件移至一个专有类里,而不是分散在项目的各个地方,整个代码架构清晰易懂、易管理。 -即便如果哪天不使用disconf,也只需要将注解去掉即可。 - -#### 基于XML的分布式配置(无代码侵入)(仅支持配置文件) - -推荐新建的项目或旧项目使用disconf时使用 - -- [Tutorial 8 基于XML的分布式配置文件管理,自动reload ](https://github.com/knightliao/disconf/wiki/Tutorial8) -- [Tutorial 5 基于XML的分布式配置文件管理,不会自动reload,对于那些比较重的资源如jdbc等,特别有用](https://github.com/knightliao/disconf/wiki/Tutorial5) - -#### 其它 - -- [Tutorial 6 disconf-web 功能详解](https://github.com/knightliao/disconf/wiki/Tutorial6) -- [Tutorial 7 可自定义的部分托管的分布式配置](https://github.com/knightliao/disconf/wiki/Tutorial7) -- [Tutorial 9 实现真正意义上的统一上线包](https://github.com/knightliao/disconf/wiki/Tutorial9) -- [Tutorial 10 实现一个配置更新下载器agent](https://github.com/knightliao/disconf/wiki/Tutorial10) -- [Tutorial 13 增加统一的回调类,unify-notify模式:灵活处理更新配置通知](https://github.com/knightliao/disconf/wiki/Tutorial13-unify-notify) -- [Tutorial 14 配置初始化或更新时,通知采用 "bean setter模式"](https://github.com/knightliao/disconf/wiki/Tutorial14-bean-setter-mode) -- [配置说明](https://github.com/knightliao/disconf/wiki/%E9%85%8D%E7%BD%AE%E8%AF%B4%E6%98%8E) - ## 大家都在使用disconf ## - [百度](20+条产品线使用) @@ -163,7 +68,7 @@ APP+环境+版本+ZK查询: - [网易](http://www.163.com/) - [苏宁易购](http://www.suning.com) (搜索中心数据处理平台) - [顺丰科技] -- [更多](https://github.com/knightliao/disconf/wiki/users) +- [更多](http://disconf.readthedocs.io/zh_CN/latest/others/src/contribute.html) ## 他人评价 @@ -179,17 +84,9 @@ APP+环境+版本+ZK查询: ![http://ww4.sinaimg.cn/bmiddle/60c9620fjw1est6pzqo68j208k05tjrm.jpg](http://ww4.sinaimg.cn/bmiddle/60c9620fjw1est6pzqo68j208k05tjrm.jpg) -开源中国社区: - -[「disconf」在「2015 年度新增开源软件排名 TOP 100(OSC开源中国提供)」中排名第16强。](http://www.oschina.net/news/69808/2015-annual-ranking-top-100-new-open-source-software) - ## 群·联系·讨论 -- disconf技术QQ群: 239203866 ; disconf技术QQ二群: 280712860 -- [媒体报道与网友教程](https://github.com/knightliao/disconf/wiki/%E5%AA%92%E4%BD%93%E6%8A%A5%E9%81%93%E4%B8%8E%E7%BD%91%E5%8F%8B%E6%95%99%E7%A8%8B) - -## 关于 - -- 搜索引擎推荐:[sov5搜索引擎, 支持谷歌网页搜索/电影搜索/资源搜索/问答搜索](http://sov5.com) -- python论坛推荐:[Django中国社区](http://www.django-china.cn/) -- [联系与赞助作者](https://github.com/knightliao/disconf/wiki/sponsor) +- disconf技术QQ群: 239203866 +- disconf技术QQ二群: 280712860 +- 有态度无广告的搜索引擎: https://www.sov5.com +- 高质量的微信公众号阅读: http://www.100weidu.com diff --git a/disconf-client/pom.xml b/disconf-client/pom.xml index 9a3ae4f07..524931042 100644 --- a/disconf-client/pom.xml +++ b/disconf-client/pom.xml @@ -10,7 +10,7 @@ com.baidu.disconf disconf-base - 2.6.35 + 2.6.36 diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/addons/properties/ReloadingPropertyPlaceholderConfigurer.java b/disconf-client/src/main/java/com/baidu/disconf/client/addons/properties/ReloadingPropertyPlaceholderConfigurer.java index 85b09bdb6..341e0163f 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/addons/properties/ReloadingPropertyPlaceholderConfigurer.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/addons/properties/ReloadingPropertyPlaceholderConfigurer.java @@ -84,7 +84,7 @@ protected String parseStringValue(String strVal, Properties props, Set visitedPl } addDependency(dynamic, placeholder); } else { - logger.warn("dynamic property outside bean property value - ignored: " + strVal); + logger.debug("dynamic property outside bean property value - ignored: " + strVal); } startIndex = endIndex - this.placeholderPrefix.length() + this.placeholderPrefix.length() + this.placeholderSuffix.length(); diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfFile.java b/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfFile.java index cc7b7123f..d16099872 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfFile.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfFile.java @@ -33,10 +33,15 @@ String version() default ""; /** - * 以"/"开头则是系统的全路径,否则则是相对于classpath的路径,默认是classpath根路径 + * 版本,默认为用户指定的app + */ + String app() default ""; + + /** + * 配置文件目标地址dir, 以"/"开头则是系统的全路径,否则则是相对于classpath的路径,默认是classpath根路径 * 注意:根路径要注意是否有权限,否则会出现找不到路径,推荐采用相对路径 * * @return */ - String copy2TargetDirPath() default ""; + String targetDirPath() default ""; } diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfItem.java b/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfItem.java index ae14e3663..cf7a85fca 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfItem.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/common/annotations/DisconfItem.java @@ -32,6 +32,11 @@ */ String env() default ""; + /** + * 版本,默认为用户指定的app + */ + String app() default ""; + /** * 版本,默认为用户指定的版本 */ diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/common/model/DisconfCenterFile.java b/disconf-client/src/main/java/com/baidu/disconf/client/common/model/DisconfCenterFile.java index 2d78a983e..64d846bd6 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/common/model/DisconfCenterFile.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/common/model/DisconfCenterFile.java @@ -41,8 +41,8 @@ public class DisconfCenterFile extends DisconfCenterBaseModel { // 文件名 private String fileName; - // 复制到指定的路径下 - private String copy2TargetDirPath; + // 配置文件 指定路径下 + private String targetDirPath; // 文件类型 private SupportFileTypeEnum supportFileTypeEnum = SupportFileTypeEnum.ANY; @@ -95,18 +95,18 @@ public void setIsTaggedWithNonAnnotationFile(boolean isTaggedWithNonAnnotationFi this.isTaggedWithNonAnnotationFile = isTaggedWithNonAnnotationFile; } - public String getCopy2TargetDirPath() { - return copy2TargetDirPath; + public String getTargetDirPath() { + return targetDirPath; } - public void setCopy2TargetDirPath(String copy2TargetDirPath) { - this.copy2TargetDirPath = copy2TargetDirPath; + public void setTargetDirPath(String targetDirPath) { + this.targetDirPath = targetDirPath; } @Override public String toString() { return "\n\tDisconfCenterFile [\n\tkeyMaps=" + keyMaps + "\n\tcls=" + cls + "\n\tfileName=" + fileName - + "\n\tcopy2TargetDirPath=" + copy2TargetDirPath + + + "\n\ttargetDirPath=" + targetDirPath + super.toString() + "]"; } @@ -141,17 +141,19 @@ public Map getKV() { * 配置文件的路径 */ public String getFilePath() { + + // 不放到classpath, 则文件路径根据 userDefineDownloadDir 来设置 if (!DisClientConfig.getInstance().enableLocalDownloadDirInClassPath) { return OsUtil.pathJoin(DisClientConfig.getInstance().userDefineDownloadDir, fileName); } - if (copy2TargetDirPath != null) { + if (targetDirPath != null) { - if (copy2TargetDirPath.startsWith("/")) { - return OsUtil.pathJoin(copy2TargetDirPath, fileName); + if (targetDirPath.startsWith("/")) { + return OsUtil.pathJoin(targetDirPath, fileName); } - return OsUtil.pathJoin(ClassLoaderUtil.getClassPath(), copy2TargetDirPath, fileName); + return OsUtil.pathJoin(ClassLoaderUtil.getClassPath(), targetDirPath, fileName); } return OsUtil.pathJoin(ClassLoaderUtil.getClassPath(), fileName); @@ -163,13 +165,13 @@ public String getFilePath() { public String getFileDir() { // 获取相对于classpath的路径 - if (copy2TargetDirPath != null) { + if (targetDirPath != null) { - if (copy2TargetDirPath.startsWith("/")) { - return OsUtil.pathJoin(copy2TargetDirPath); + if (targetDirPath.startsWith("/")) { + return OsUtil.pathJoin(targetDirPath); } - return OsUtil.pathJoin(ClassLoaderUtil.getClassPath(), copy2TargetDirPath); + return OsUtil.pathJoin(ClassLoaderUtil.getClassPath(), targetDirPath); } return ClassLoaderUtil.getClassPath(); diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/fetcher/FetcherMgr.java b/disconf-client/src/main/java/com/baidu/disconf/client/fetcher/FetcherMgr.java index 46abd6978..195ffabcf 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/fetcher/FetcherMgr.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/fetcher/FetcherMgr.java @@ -14,11 +14,11 @@ public interface FetcherMgr { String getValueFromServer(String url) throws Exception; /** - * 下载配置文件, remoteUrl是目标 url, 下载到预定义的文件夹,并复制到classpath文件夹下 + * 下载配置文件, remoteUrl是目标 url, 下载到预定义的文件夹,并 下载到 targetDirPath 目录下 * * @throws Exception */ - String downloadFileFromServer(String url, String fileName, String copy2TargetDirPath) throws Exception; + String downloadFileFromServer(String url, String fileName, String targetDirPath) throws Exception; /** * 释放资源 diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/mybeans/DisconfMgrJustHostFileBean.java b/disconf-client/src/main/java/com/baidu/disconf/client/mybeans/DisconfMgrJustHostFileBean.java deleted file mode 100644 index 0c08f490d..000000000 --- a/disconf-client/src/main/java/com/baidu/disconf/client/mybeans/DisconfMgrJustHostFileBean.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.baidu.disconf.client.mybeans; - -import java.util.HashSet; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.core.Ordered; -import org.springframework.core.PriorityOrdered; - -import com.baidu.disconf.client.store.inner.DisconfCenterHostFilesStore; - -/** - * 进行配置文件托管的bean: 启动时自动下载此配置;更改配置时,亦会自动下载此配置。不会进行java bean类的注入。 - * - * @author knightliao - */ -@Deprecated -public class DisconfMgrJustHostFileBean implements BeanDefinitionRegistryPostProcessor, PriorityOrdered { - - protected static final Logger log = LoggerFactory.getLogger(DisconfMgrJustHostFileBean.class); - - private Set justHostFiles = new HashSet(); - - @Override - public String toString() { - return "DisconfMgrJustHostFileBean [justHostFiles=" + justHostFiles + "]"; - } - - public Set getJustHostFiles() { - return justHostFiles; - } - - public void setJustHostFiles(Set justHostFiles) { - this.justHostFiles = justHostFiles; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - } - - @Override - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE; - } - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - - if (justHostFiles != null) { - - for (String file : justHostFiles) { - if (file != null) { - String fileName = file.trim(); - log.debug("disconf no-reloadable file: " + fileName); - // 添加到配置文件托管列表里 - DisconfCenterHostFilesStore.getInstance().addJustHostFile(fileName); - } - } - } - } - -} \ No newline at end of file diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerFileMgrImpl.java b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerFileMgrImpl.java index 8c1f63d0a..f93b4078a 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerFileMgrImpl.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerFileMgrImpl.java @@ -96,8 +96,8 @@ private static DisconfCenterFile transformScanFile(Class disconfFileClass, Se // file name disconfCenterFile.setFileName(disconfFileAnnotation.filename()); - // copy 2 target path - disconfCenterFile.setCopy2TargetDirPath(disconfFileAnnotation.copy2TargetDirPath().trim()); + // config file target dir path + disconfCenterFile.setTargetDirPath(disconfFileAnnotation.targetDirPath().trim()); // file type disconfCenterFile.setSupportFileTypeEnum(SupportFileTypeEnum.getByFileName(disconfFileAnnotation.filename())); @@ -105,7 +105,8 @@ private static DisconfCenterFile transformScanFile(Class disconfFileClass, Se // // disConfCommonModel DisConfCommonModel disConfCommonModel = - makeDisConfCommonModel(disconfFileAnnotation.env(), disconfFileAnnotation.version()); + makeDisConfCommonModel(disconfFileAnnotation.app(), disconfFileAnnotation.env(), disconfFileAnnotation + .version()); disconfCenterFile.setDisConfCommonModel(disConfCommonModel); // Remote URL diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerItemMgrImpl.java b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerItemMgrImpl.java index 940eebd6e..44c635ae5 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerItemMgrImpl.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerItemMgrImpl.java @@ -122,7 +122,8 @@ private static DisconfCenterItem transformScanItem(Method method) { // // disConfCommonModel - DisConfCommonModel disConfCommonModel = makeDisConfCommonModel(disconfItem.env(), disconfItem.version()); + DisConfCommonModel disConfCommonModel = makeDisConfCommonModel(disconfItem.app(), disconfItem.env(), + disconfItem.version()); disconfCenterItem.setDisConfCommonModel(disConfCommonModel); // Disconf-web url diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerMgrImplBase.java b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerMgrImplBase.java index 9ee28823b..4a6fb2ba4 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerMgrImplBase.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerMgrImplBase.java @@ -12,12 +12,16 @@ public class StaticScannerMgrImplBase { /** * env/version 默认是应用整合设置的,但用户可以在配置中更改它 */ - protected static DisConfCommonModel makeDisConfCommonModel(String env, String version) { + protected static DisConfCommonModel makeDisConfCommonModel(String app, String env, String version) { DisConfCommonModel disConfCommonModel = new DisConfCommonModel(); // app - disConfCommonModel.setApp(DisClientConfig.getInstance().APP); + if (!app.isEmpty()) { + disConfCommonModel.setApp(app); + } else { + disConfCommonModel.setApp(DisClientConfig.getInstance().APP); + } // env if (!env.isEmpty()) { diff --git a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerNonAnnotationFileMgrImpl.java b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerNonAnnotationFileMgrImpl.java index 7b7c2367a..cb6e5914e 100644 --- a/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerNonAnnotationFileMgrImpl.java +++ b/disconf-client/src/main/java/com/baidu/disconf/client/scan/inner/statically/impl/StaticScannerNonAnnotationFileMgrImpl.java @@ -89,7 +89,7 @@ public static DisconfCenterBaseModel getDisconfCenterFile(String fileName) { // // disConfCommonModel - DisConfCommonModel disConfCommonModel = makeDisConfCommonModel("", ""); + DisConfCommonModel disConfCommonModel = makeDisConfCommonModel("", "", ""); disconfCenterFile.setDisConfCommonModel(disConfCommonModel); // Remote URL diff --git a/disconf-client/src/test/java/com/baidu/disconf/client/test/fetcher/inner/restful/RestfulMgrMock.java b/disconf-client/src/test/java/com/baidu/disconf/client/test/fetcher/inner/restful/RestfulMgrMock.java index d2b1aa35f..b67b8c771 100644 --- a/disconf-client/src/test/java/com/baidu/disconf/client/test/fetcher/inner/restful/RestfulMgrMock.java +++ b/disconf-client/src/test/java/com/baidu/disconf/client/test/fetcher/inner/restful/RestfulMgrMock.java @@ -60,7 +60,7 @@ public T getJsonData(Class clazz, RemoteUrl remoteUrl, int retryTimes, in */ @Mock public String downloadFromServer(RemoteUrl remoteUrl, String fileName, String localFileDir, String localFileDirTemp, - String copy2TargetDirPath, boolean download2Classpath, int retryTimes, + String targetDirPath, boolean download2Classpath, int retryTimes, int retrySleepSeconds) throws Exception { diff --git a/disconf-core/pom.xml b/disconf-core/pom.xml index 5a88a75a7..39145c935 100644 --- a/disconf-core/pom.xml +++ b/disconf-core/pom.xml @@ -13,7 +13,7 @@ com.baidu.disconf disconf-base - 2.6.35 + 2.6.36 diff --git a/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/RestfulMgr.java b/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/RestfulMgr.java index 92ceb91d2..7389dbf75 100644 --- a/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/RestfulMgr.java +++ b/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/RestfulMgr.java @@ -23,17 +23,17 @@ public interface RestfulMgr { T getJsonData(Class clazz, RemoteUrl remoteUrl, int retryTimes, int retrySleepSeconds) throws Exception; /** - * @param remoteUrl 远程地址 - * @param fileName 文件名 - * @param localFileDir 本地文件地址 - * @param copy2TargetDirPath 下载完后,还需要复制到此文件夹下 + * @param remoteUrl 远程地址 + * @param fileName 文件名 + * @param localFileDir 本地文件地址 + * @param targetDirPath 下载完后,配置文件放到此目录下 * * @return 如果是放到Classpath目录下,则返回相对Classpath的路径,如果不是,则返回全路径 * * @throws Exception */ String downloadFromServer(RemoteUrl remoteUrl, String fileName, String localFileDir, String localFileDirTemp, - String copy2TargetDirPath, + String targetDirPath, boolean enableLocalDownloadDirInClassPath, int retryTimes, int retrySleepSeconds) throws Exception; diff --git a/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/core/RemoteUrl.java b/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/core/RemoteUrl.java index d26b1d470..5746106a8 100644 --- a/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/core/RemoteUrl.java +++ b/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/core/RemoteUrl.java @@ -32,8 +32,11 @@ public RemoteUrl(String url, List serverList) { try { - if (!server.contains("http://")) { - server = "http://" + server; + if (!server.startsWith("http://")) { + if (server.startsWith("https://")) { + } else { + server = "http://" + server; + } } urls.add(new URL(server + url)); diff --git a/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/impl/RestfulMgrImpl.java b/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/impl/RestfulMgrImpl.java index 63ec929e5..00212dd3a 100644 --- a/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/impl/RestfulMgrImpl.java +++ b/disconf-core/src/main/java/com/baidu/disconf/core/common/restful/impl/RestfulMgrImpl.java @@ -76,10 +76,10 @@ public T getJsonData(Class clazz, RemoteUrl remoteUrl, int retryTimes, in } /** - * @param remoteUrl 远程地址 - * @param fileName 文件名 - * @param localFileDir 本地文件地址 - * @param copy2TargetDirPath 下载完后,还需要复制到此文件夹下 + * @param remoteUrl 远程地址 + * @param fileName 文件名 + * @param localFileDir 本地文件地址 + * @param targetDirPath 下载完后,配置文件指定的文件目录 * @param retryTimes * @param retrySleepSeconds * @@ -89,7 +89,7 @@ public T getJsonData(Class clazz, RemoteUrl remoteUrl, int retryTimes, in */ @Override public String downloadFromServer(RemoteUrl remoteUrl, String fileName, String localFileDir, String localFileDirTemp, - String copy2TargetDirPath, boolean enableLocalDownloadDirInClassPath, + String targetDirPath, boolean enableLocalDownloadDirInClassPath, int retryTimes, int retrySleepSeconds) throws Exception { @@ -110,12 +110,12 @@ public String downloadFromServer(RemoteUrl remoteUrl, String fileName, String lo localFile = transfer2SpecifyDir(tmpFilePathUniqueFile, localFileDir, fileName, false); // mv 到指定目录 - if (copy2TargetDirPath != null) { + if (targetDirPath != null) { // - if (enableLocalDownloadDirInClassPath || !copy2TargetDirPath.equals(ClassLoaderUtil.getClassPath + if (enableLocalDownloadDirInClassPath || !targetDirPath.equals(ClassLoaderUtil.getClassPath ())) { - localFile = transfer2SpecifyDir(tmpFilePathUniqueFile, copy2TargetDirPath, fileName, true); + localFile = transfer2SpecifyDir(tmpFilePathUniqueFile, targetDirPath, fileName, true); } } @@ -129,7 +129,7 @@ public String downloadFromServer(RemoteUrl remoteUrl, String fileName, String lo // // 判断是否下载失败 // - + if (localFile == null || !localFile.exists()) { throw new Exception("target file cannot be found! " + fileName); } @@ -184,20 +184,20 @@ private File retryDownload(String localFileDirTemp, String fileName, RemoteUrl r * copy/mv 到指定目录 * * @param srcFile - * @param copy2TargetDirPath + * @param targetDirPath * @param fileName * * @return * * @throws Exception */ - private File transfer2SpecifyDir(File srcFile, String copy2TargetDirPath, String fileName, + private File transfer2SpecifyDir(File srcFile, String targetDirPath, String fileName, boolean isMove) throws Exception { // make dir - OsUtil.makeDirs(copy2TargetDirPath); + OsUtil.makeDirs(targetDirPath); - File targetPath = new File(OsUtil.pathJoin(copy2TargetDirPath, fileName)); + File targetPath = new File(OsUtil.pathJoin(targetDirPath, fileName)); // 从下载文件 复制/mv 到targetPath 原子性的做转移 OsUtil.transferFileAtom(srcFile, targetPath, isMove); return targetPath; diff --git a/disconf-core/src/main/java/com/baidu/disconf/core/common/utils/ClassLoaderUtil.java b/disconf-core/src/main/java/com/baidu/disconf/core/common/utils/ClassLoaderUtil.java index ab7b14b7d..038cec7bf 100644 --- a/disconf-core/src/main/java/com/baidu/disconf/core/common/utils/ClassLoaderUtil.java +++ b/disconf-core/src/main/java/com/baidu/disconf/core/common/utils/ClassLoaderUtil.java @@ -39,7 +39,8 @@ private ClassLoaderUtil() { java.net.URL url = loader.getResource(""); // get class path - classPath = url.getPath(); + File f=new File(url.toURI()); + classPath = f.getAbsolutePath(); classPath = URLDecoder.decode(classPath, "utf-8"); // 如果是jar包内的,则返回当前路径 diff --git a/disconf-web/pom.xml b/disconf-web/pom.xml index cb8fb070f..c6628aab5 100644 --- a/disconf-web/pom.xml +++ b/disconf-web/pom.xml @@ -12,7 +12,7 @@ com.baidu.disconf disconf-base - 2.6.35 + 2.6.36 diff --git a/disconf-web/sql/readme.md b/disconf-web/sql/readme.md index e4846417e..63e7acaa3 100644 --- a/disconf-web/sql/readme.md +++ b/disconf-web/sql/readme.md @@ -3,3 +3,4 @@ - 0-init_table.sql create db,tables - 1-init_data.sql create data - 201512/20151225.sql patch +- 20160701/20160701.sql patch diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..759d7e53c --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +build +*.iml +*.pyc \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..8a15d4cd1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,223 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) + $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/disconf.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/disconf.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/disconf" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/disconf" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..17c564958 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,6 @@ +# disconf-readthedocs + +readthedocs for disconf + +- docs: http://disconf.readthedocs.io +- source: https://github.com/knightliao/disconf \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 000000000..b6467e31a --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,272 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. epub3 to make an epub3 + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\disconf.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\disconf.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "epub3" ( + %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/docs/myMake.sh b/docs/myMake.sh new file mode 100644 index 000000000..81a2ccf87 --- /dev/null +++ b/docs/myMake.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +md_list=`find source -iname '*.md'` + +for md_item in ${md_list} +do + echo "============== process" ${md_item} "==============" + + target_file=$(echo ${md_item}| cut -d'.' -f 1 ) + target_file_dir=$(dirname ${target_file} ) + target_file_name=$(basename ${target_file}) + target_file_name=${target_file_dir}/src/${target_file_name} + + target_file=${target_file_name}".rst" + if [ -f ${target_file} ] + then + echo rm ${target_file} + rm ${target_file} + fi + echo pandoc --from=markdown --to=rst --output=${target_file} ${md_item} + pandoc -V lang=zh_CN --from=markdown_github --to=rst --output=${target_file} ${md_item} +done + +make html + diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 000000000..cc2d25c9f --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# +# disconf documentation build configuration file, created by +# sphinx-quickstart on Wed Feb 17 15:48:38 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +from recommonmark.parser import CommonMarkParser + +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +source_parsers = { + '.md': CommonMarkParser, +} + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = ['.rst'] + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'disconf' +copyright = u'2016, knightliao' +author = u'knightliao' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'2.6.36' +# The full version, including alpha/beta/rc tags. +release = u'2.6.36' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# html_title = u'disconf v0.0.1' + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'disconfdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + + # Latex figure (float) alignment + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'disconf.tex', u'disconf Documentation', + u'knightliao', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'disconf', u'disconf Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'disconf', u'disconf Documentation', + author, 'disconf', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False diff --git a/docs/source/config/client-config.md b/docs/source/config/client-config.md new file mode 100644 index 000000000..157668e90 --- /dev/null +++ b/docs/source/config/client-config.md @@ -0,0 +1,108 @@ +client配置 +======= + +##Disconf-Client## + +### 配置文件 disconf.properties 说明 ### + +所有配置均可以通过 命令行 `-Dname=value` 参数传入。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
配置项说明是否必填默认值
disconf.conf_server_host配置服务器的 HOST,用逗号分隔 ,示例:127.0.0.1:8000,127.0.0.1:8000必填
disconf.appAPP 请采用 产品线_服务名 格式 优先读取命令行参数,然后再读取此文件的值
disconf.version版本号, 请采用 X_X_X_X 格式 默认为 DEFAULT_VERSION。优先读取命令行参数,然后再读取此文件的值,最后才读取默认值。
disconf.enable.remote.conf是否使用远程配置文件,true(默认)会从远程获取配置, false则直接获取本地配置false
disconf.env环境默认为 DEFAULT_ENV。优先读取命令行参数,然后再读取此文件的值,最后才读取默认值
disconf.ignore忽略的分布式配置,用空格分隔
disconf.debug调试模式。调试模式下,ZK超时或断开连接后不会重新连接(常用于client单步debug)。非调试模式下,ZK超时或断开连接会自动重新连接。false
disconf.conf_server_url_retry_times获取远程配置 重试次数,默认是3次3
disconf.conf_server_url_retry_sleep_seconds获取远程配置 重试时休眠时间,默认是5秒 5
disconf.user_define_download_dir用户定义的下载文件夹, 远程文件下载后会放在这里。注意,此文件夹必须有有权限,否则无法下载到这里./disconf/download
disconf.enable_local_download_dir_in_class_path下载的文件会被迁移到classpath根路径下,强烈建议将此选项置为 true(默认是true) true
+ +### 自定义 disconf.properties 文件的路径 + +一般情况下,disconf.properties 应该放在应用程序的根目录下,如果想自定义路径可以使用: + + -Ddisconf.conf=/tmp/disconf.properties + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/config/index.rst b/docs/source/config/index.rst new file mode 100644 index 000000000..935b77d94 --- /dev/null +++ b/docs/source/config/index.rst @@ -0,0 +1,8 @@ +配置项 +===== + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/client-config diff --git a/docs/source/config/src/client-config.rst b/docs/source/config/src/client-config.rst new file mode 100644 index 000000000..0f6f1282a --- /dev/null +++ b/docs/source/config/src/client-config.rst @@ -0,0 +1,97 @@ +client配置 +========== + +Disconf-Client +-------------- + +配置文件 disconf.properties 说明 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +所有配置均可以通过 命令行 ``-Dname=value`` 参数传入。 + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
配置项说明是否必填默认值
disconf.conf_server_host配置服务器的 HOST,用逗号分隔 ,示例:127.0.0.1:8000,127.0.0.1:8000必填
disconf.appAPP 请采用 产品线_服务名 格式 优先读取命令行参数,然后再读取此文件的值
disconf.version版本号, 请采用 X_X_X_X 格式 默认为 DEFAULT_VERSION。优先读取命令行参数,然后再读取此文件的值,最后才读取默认值。
disconf.enable.remote.conf是否使用远程配置文件,true(默认)会从远程获取配置, false则直接获取本地配置false
disconf.env环境默认为 DEFAULT_ENV。优先读取命令行参数,然后再读取此文件的值,最后才读取默认值
disconf.ignore忽略的分布式配置,用空格分隔
disconf.debug调试模式。调试模式下,ZK超时或断开连接后不会重新连接(常用于client单步debug)。非调试模式下,ZK超时或断开连接会自动重新连接。false
disconf.conf_server_url_retry_times获取远程配置 重试次数,默认是3次3
disconf.conf_server_url_retry_sleep_seconds获取远程配置 重试时休眠时间,默认是5秒 5
disconf.user_define_download_dir用户定义的下载文件夹, 远程文件下载后会放在这里。注意,此文件夹必须有有权限,否则无法下载到这里./disconf/download
disconf.enable_local_download_dir_in_class_path下载的文件会被迁移到classpath根路径下,强烈建议将此选项置为 true(默认是true) true
+ +自定义 disconf.properties 文件的路径 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +一般情况下,disconf.properties +应该放在应用程序的根目录下,如果想自定义路径可以使用: + +:: + + -Ddisconf.conf=/tmp/disconf.properties diff --git "a/docs/source/design/Zookeeper\345\274\202\345\270\270\350\200\203\350\231\221.md" "b/docs/source/design/Zookeeper\345\274\202\345\270\270\350\200\203\350\231\221.md" new file mode 100644 index 000000000..265307f88 --- /dev/null +++ "b/docs/source/design/Zookeeper\345\274\202\345\270\270\350\200\203\350\231\221.md" @@ -0,0 +1,72 @@ +disconf的Zookeeper异常考虑 +========== + +##disconf-web的ZK异常处理## + +disconf-web可以完全保证在任何情况下,与ZK集群的自动连接。 + +下面按情况进行分析: + +###服务启动前,zk连接不上: + +- 开始连接不上: + - apache ZK client自身会自动(永久)去连接ZK server. 但是一直连接不上。 + - 因此,web上所有操作均会失败,抛大异常,请求失败,只会重试一次,不会重试多次 +- 后面突然连接上了: + - apache ZK client 收到server SyncConnected消息。 + - 这时所有操作均成功 +- 后面又突然连接不上了: + - apache ZK client 收到server Disconnected 消息。 + - 这时,apache ZK client自身会自动(永久)去连接ZK server. 但是一直连接不上。 + - 这时 web 上 所有操作均会失败,抛大异常,请求失败,只会重试一次,不会重试多次 +- 后面突然连接上了: + - apache ZK client 收到server Expired 消息。 + - 这时表示会话丢失啦,apache ZK client 自动断开与Server的连接,表示此时让你来处理,因为它不知道应该如何处理。 + - 这时,disconf-core会reconnect zkserver,重新建立会话。 + - 成功后,apache ZK client 收到server SyncConnected 消息。表示连接成功 +- 后面又突然连接不上了: + - apache ZK client 收到server Disconnected 消息。 + - 这时,apache ZK client自身会自动(永久)去连接ZK server. 但是一直连接不上。 + - 这时 web 上 所有操作均会失败,抛大异常,请求失败,只会重试一次,不会重试多次 + +###服务启动前,zk连接上了: + +- 开始连接: + - apache ZK client 收到server SyncConnected消息。 + - 这时所有操作均成功功 +- 后面又突然连接不上了…… (与上面分析一样,此不再赘述) + +###注意 + +ZK一般需要以集群的形式提供出来。假设有N台ZK, + +- 只要至少有一台ZK存活,disconf-web就可以正常工作。而且永远不会收到 server Expired 的消息。 +- 只要有一台ZK死亡,disconf-web就会收到 Disconnected 消息。但是系统仍可以继续工作。 +- 如果所有zk都死亡,那么disconf-web会收到 Disconnected 消息。只要有一台存活,disconf-web就会收到 + +##disconf-client的ZK异常处理## + +disconf-client可以完全保证: **如果在启动程序时保证ZK集群是可用的**,那么,就可以保证在任何情况下,与ZK集群的自动连接。 + +下面按情况进行分析: + +###程序启动前,zk连接不上: + +这时disconf-client无法在ZK上注册信息。这是必须禁止发生的情况。也是disconf-client无法支持的情况。 + +一旦发生这种情况,请先恢复ZK集群,再启动你的程序。 + +###程序启动前,zk连接上了: + +如果在程序启动过程中,ZK是正常的,那么,disconf-client可以保证与ZK连接的自动性。 + +- 只要集群有一台还存活着,你的程序配置还是受disconf托管。 +- 如果集群所有机器均死亡,这时你的程序将游离于disconf之外。只要集群中有任何一台ZK机器重新开启,那么 你的程序将重新 由disconf进行托管。 + +###注意 + +disconf-client必须保证在程序在启动时,ZK集群的可用性。 + + + + diff --git "a/docs/source/design/disconf-client\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.md" "b/docs/source/design/disconf-client\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.md" new file mode 100644 index 000000000..1eba71e7b --- /dev/null +++ "b/docs/source/design/disconf-client\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.md" @@ -0,0 +1,535 @@ +Disconf-client详细设计文档 +======= + +本文档主要阐述了版本 Disconf-Client 的设计。、 + +## 程序运行流程图 ## + +### 版本2.0的设计 ### + +![](http://ww3.sinaimg.cn/bmiddle/60c9620fjw1eqi7tnuic8j20l50g7acs.jpg) + +[点击查看大图 ](http://ww3.sinaimg.cn/mw1024/60c9620fjw1eqi7tnuic8j20l50g7acs.jpg) + +**运行流程详细介绍:** + +- **启动事件A**:以下按顺序发生。 + - A1:扫描静态注解类数据,并注入到配置仓库里。 + - A2:根据仓库里的配置文件、配置项,到 disconf-web 平台里下载配置数据。 + - A3:将下载得到的配置数据值注入到仓库里。 + - A4:根据仓库里的配置文件、配置项,去ZK上监控结点。 + - A5:根据XML配置定义,到 disconf-web 平台里下载配置文件,放在仓库里,并监控ZK结点。 + - A6:A1-A5均是处理静态类数据。A6是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。 +- **更新配置事件B**:以下按顺序发生。 + - B1:管理员在 Disconf-web 平台上更新配置。 + - B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。 + - B3:ZK通知 Disconf-cient 模块。 + - B4:与A2一样。唯一不同的是它只处理一个配置文件或者一个配置项,而事件A2则是处理所有配置文件和配置项。下同。 + - B5:与A3一样。 + - B6:基本与A4一样,区别是,这里还会将配置的新值注入到配置实体里。 + +### 完全版的设计 ### + +![](http://ww3.sinaimg.cn/bmiddle/60c9620fjw1eqj81no7shj20l50h2q65.jpg) + +[点击查看大图 ](http://ww3.sinaimg.cn/mw1024/60c9620fjw1eqj81no7shj20l50h2q65.jpg + +**运行流程详细介绍:** + +与2.0版本的主要区别是支持了:主备分配功能/主备切换事件。 + +- **启动事件A**:以下按顺序发生。 + - A3:扫描静态注解类数据,并注入到配置仓库里。 + - A4+A2:根据仓库里的配置文件、配置项,去 disconf-web 平台里下载配置数据。这里会有主备竞争 + - A5:将下载得到的配置数据值注入到仓库里。 + - A6:根据仓库里的配置文件、配置项,去ZK上监控结点。 + - A7+A2:根据XML配置定义,到 disconf-web 平台里下载配置文件,放在仓库里,并监控ZK结点。这里会有主备竞争。 + - A8:A1-A6均是处理静态类数据。A7是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。 +- **更新配置事件B**:以下按顺序发生。 + - B1:管理员在 Disconf-web 平台上更新配置。 + - B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。 + - B3:ZK通知 Disconf-cient 模块。 + - B4:与A4一样。 + - B5:与A5一样。 + - B6:基本与A4一样,唯一的区别是,这里还会将配置的新值注入到配置实体里。 +- **主备机切换事件C**:以下按顺序发生。 + - C1:发生主机挂机事件。 + - C2:ZK通知所有被影响到的备机。 + - C4:与A2一样。 + - C5:与A4一样。 + - C6:与A5一样。 + - C7:与A6一样。 + +## 类设计图 ## + +![](http://ww4.sinaimg.cn/bmiddle/60c9620fgw1ej0ycv2fjbj21ao0u8441.jpg) + +[查看大图](http://ww4.sinaimg.cn/mw1024/60c9620fgw1ej0ycv2fjbj21ao0u8441.jpg) + +**Disconf-client包括的大模块有:** + +- scan 配置扫描模块 +- core 配置核心处理模块 +- fetch 配置抓取模块 +- watch 配置监控模块 +- store 配置仓库模块 +- addons 配置reload模块 + +**各个模块均采用以下设计模式来进设计:** + +- 各个模块均以接口的方式对外暴露,松耦合,强内聚 +- 各个模块均提供工厂类由其它模块来进行获取实例,实例的操纵方式均采用接口方式。 +- 对于配置文件和配置项,采用类扩展的方法来避免if else判断。 + +## Disconf-client 的启动 ## + +启动分成两步,由两个Bean来实现 + + + + + + + +这里 com.baidu.disconf.dem 是要扫描的类。 + +第一步由Bean com.baidu.disconf.client.DisconfMgrBean 来控制。第二步由 com.baidu.disconf.client.DisconfMgrBeanSecond 控制。 + +### 第一步:com.baidu.disconf.client.DisconfMgrBean ### + +此Bean实现了BeanFactoryPostProcessor和PriorityOrdered接口。它的Bean初始化Order是最高优先级的。 + +因此,当Spring扫描了所有的Bean信息后,在所有Bean初始化(init)之前,DisconfMgrBean的postProcessBeanFactory方法将被调用,在这里,Disconf-Client会进行第一次扫描。 + +扫描按顺序做了以下几个事情: + +1. 初始化Disconf-client自己的配置模块。 +2. 初始化Scan模块。 +3. 初始化Core模块,并极联初始化Watch,Fetcher,Restful模块。 +4. 扫描用户类,整合分布式配置注解相关的静态类信息至配置仓库里。 +5. 执行Core模块,从disconf-web平台上下载配置数据:配置文件下载到本地,配置项直接下载。 +6. 配置文件和配置项的数据会注入到配置仓库里。 +7. 使用watch模块为所有配置关联ZK上的结点。 + +其中对配置的处理详细为: + +![](http://ww3.sinaimg.cn/bmiddle/60c9620fgw1ej1x5tfvzgj20pj141421.jpg) + +[查看大图](http://ww3.sinaimg.cn/mw1024/60c9620fgw1ej1x5tfvzgj20pj141421.jpg) + +### 第二步:com.baidu.disconf.client.DisconfMgrBeanSecond ### + +DisconfMgrBean的扫描主要是静态数据的初始化,并未涉及到动态数据。DisconfMgrBeanSecond Bean则是将一些动态的数据写到仓库里。 + +本次扫描按顺序做了以下几个事情: + +1. 将配置更新回调实例放到配置仓库里 +2. 为配置实例注入值。 + +![](http://ww3.sinaimg.cn/bmiddle/60c9620fgw1ej1x5ve5w6j20pj11xwj4.jpg) + +[查看大图](http://ww3.sinaimg.cn/mw1024/60c9620fgw1ej1x5ve5w6j20pj11xwj4.jpg) + +## 分布式配置的实现 ## + +下面将 分别详细阐述 分布式配置文件 和 分布式配置项 的实现方式。 + +由于目前版本只支持 Spring编程方式,因此,以下均只阐述Spring编程下的实现方式。 + +### 注解式实现 ### + +#### 分布式配置文件的实现 #### + +**定义分布式配置文件类** + +对于配置文件,我们必须实现一个Java类来表示此 分布式配置文件。如: + + package com.example.disconf.demo.config; + + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @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; + } + } + + +对于此Java类,它必须是Spring托管的。此配置文件是redis.properties。 + +此配置类必须标注为 @DisconfFile,标识它是一个分布式配置文件。且必须指定文件名。 + +此配置类含有两个配置项,分别是host和port。这两个变量必须有 get 方法。且get方法名必须是符合JavaBean规范的。 + +我们通过在这两个变量的 get 方法上添加 @DisconfFileItem 注解来标注它是分布式配置文件里的配置项。必须指定name参数,表示配置文件里的KEY值。associateField值是可选的,表示此get方法相对应的域的名字。 + +**Disconf-client优先启动,并从平台上下载配置文件:** + +应用程序启动时,当Spring容器扫描了所有Java Bean却还未初始化这些Bean时,disconf-client 模块会优先开始初始化(最高优先级)。它会将 配置文件名、配置项名记录在配置仓库里,并去 disconf-web 平台下载配置文件至classpath目录下。并且,还会到ZK上生成相应的结点。 + +接着Spring开始初始化用户定义的SpringBean。由于配置文件已经被正确下载至Classpath路径下,因此,JavaBean的配置文件使用的是分布式配置文件,而非本地的配置文件。 + +**待SpringBean初始化后,Disconf-client会获取配置更新回调类实例:** + +此时,Spring上的所有Bean均已被init。Disconf-client模块会再次运行,这时它会去获取用户撰写的配置更新回调函数类实例。 + +一个配置更新回调函数通常是这样撰写的: + + package com.example.disconf.demo.service.callbacks; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfUpdateService; + import com.baidu.disconf.client.common.update.IDisconfUpdate; + import com.example.disconf.demo.config.Coefficients; + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.service.SimpleRedisService; + + /** + * 更新Redis配置时的回调函数 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + @DisconfUpdateService(classes = {JedisConfig.class}, itemKeys = {Coefficients.key}) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisServiceUpdateCallback.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + /** + * + */ + public void reload() throws Exception { + + simpleRedisService.changeJedis(); + } + + } + +此类必须实现接口IDisconfUpdate,它可以不必是Java托管的。如果是SpringBean,则disconf-client会从Spring容器里获取此Bean。如果它不是SpringBean,disconf-client就会new一个实例出来。 + +使用SpringBean来定义此类的好处是,我们可以在此类中使用@Autowired来使用其它SpringBean。比较方便些。 + +disconf-client根据注解@DisconfUpdateService 以配置文件为Key,将回调函数实例列表放在此Key的Map里。当配置文件更新时,这些回调函数实例就会被按顺序执行。 + +**配置文件更新时,分布式配置文件会重新被下载:** + +当配置文件更新时,disconf-client便会重新从 disconf-web 平台下载配置文件,并重新将值放在配置仓库里。并按顺序进行调用回调函数类的 reload() 方法。 + +**如何使用分布式配置文件类:** + +在上面我们说到,配置文件类中的配置项必须有 get 方法,并且必须有 @DisconfFileItem 注解。 + +在 get 上面添加注解的原因就是为了做切面。 + +disconf-cient使用Spring AOP拦截 系统里所有含有@DisconfFileItem注解的 get 方法,把所有此类请求都定向到用户程序的配置仓库中去获取。 + +通过这种方式,我们可以实现统一的、集中式的在配置仓库里去获取配置文件数据。这是一种简洁的实现方式。 + +#### 分布式配置项的实现 #### + +配置项相对于配置文件,比较灵活。我们可以在任何SpringBean里添加配置项。 + +如以下是在一个配置文件类里添加配置项: + + package com.example.disconf.demo.config; + + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + import com.baidu.disconf.client.common.annotations.DisconfItem; + + /** + * 金融系数文件 + */ + @Service + @DisconfFile(filename = "coefficients.properties") + public class Coefficients { + + public static final String key = "discountRate"; + + @Value(value = "2.0d") + private Double discount; + + private double baiFaCoe; + + private double yuErBaoCoe; + + /** + * 阿里余额宝的系数, 分布式文件配置 + * + * @return + */ + @DisconfFileItem(name = "coe.baiFaCoe") + public double getBaiFaCoe() { + return baiFaCoe; + } + + public void setBaiFaCoe(double baiFaCoe) { + this.baiFaCoe = baiFaCoe; + } + + /** + * 百发的系数, 分布式文件配置 + * + * @return + */ + @DisconfFileItem(name = "coe.yuErBaoCoe") + public double getYuErBaoCoe() { + return yuErBaoCoe; + } + + public void setYuErBaoCoe(double yuErBaoCoe) { + this.yuErBaoCoe = yuErBaoCoe; + } + + /** + * 折扣率,分布式配置 + * + * @return + */ + @DisconfItem(key = key) + public Double getDiscount() { + return discount; + } + + public void setDiscount(Double discount) { + this.discount = discount; + } + } + +或者,我们也可以在一个Service类里添加配置项: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.Coefficients; + + /** + * 金融宝服务,计算一天赚多少钱 + * + * @author liaoqiqi + * @version 2014-5-16 + */ + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory.getLogger(BaoBaoService.class); + + public static final String key = "moneyInvest"; + + @Value(value = "2000d") + private Double moneyInvest; + + @Autowired + private Coefficients coefficients; + + /** + * 计算百发一天赚多少钱 + * + * @return + */ + public double calcBaiFa() { + return coefficients.getBaiFaCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * k 计算余额宝一天赚多少钱 + * + * @return + */ + public double calcYuErBao() { + return coefficients.getYuErBaoCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * 投资的钱,分布式配置
+ *
+ * 这里切面无法生效,因为SpringAOP不支持。
+ * 但是这里还是正确的,因为我们会将值注入到Bean的值里. + * + * @return + */ + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + + public void setMoneyInvest(Double moneyInvest) { + this.moneyInvest = moneyInvest; + } + } + + +采用哪种方式,由用户选择。 + +值得注意的是,在第二种实现中,它的方法calcBaiFa() 时调用了 getMoneyInvest() 方法。 getMoneyInvest() 是配置项的get方法,它添加了@DisconfItem注解,表明它是一个配置项,并且会被切面拦截,moneyInvest的值会在配置仓库里获取。但是,可惜的是,SpringAOP是无法拦截"Call myself"方法的。也就是说getMoneyInvest()是无法被切面拦截到的。 + +为了解决此问题,在实现中,我们不仅将它的值 注入到配置仓库中,而且还注入到配置项所在类的实例里。因此,在上面第二种实现中,虽然 getMoneyInvest() 方法无法被拦截,但是它返回的还是正确的分布式值的。 + +配置文件也一样,配置值亦会注入到配置文件类实体中。 + +#### 非Spring编程的实现 #### + +在非Spring方式下,无法使用AOP切面编程,因此无法统一的拦截配置数据请求。 + +在这种情况下,用户配置类的实现有两种方式: + +1. 配置类的域是static。用户直接访问这些域便可以获取得到配置类数据。 +2. 配置类使用单例。用户通过单例访问配置获取配置类数据。 + +注意:此两种方式均无法自动避免“配置读取不一致问题”。 + +当事件发生时,用户程序处理配置的方式是: + +1. 配置文件更新时,系统会自动去下载配置文件存储到本地,并存储到配置仓库。对于static变量,系统会自动注入到配置类中。对于使用单例实现方式,用户必须在回调函数中进行用户配置类的更新。 +2. 配置项更新时,与配置文件更新一样。 + +#### Zookeeper的目录存储结构 #### + + |----disconf + |----app1_version1_env1 + |----file + |----confA.properties + |----item + |----keyA + |----app2_version2_env2 + |----file + |----conf2.properties + |----item + |----key2 + +### 基于XML的实现 + +虽然注解式编程简单、直观,易维护,但是,它是具有一定的代码侵入性的。 +disconf考虑到有些用户不想写代码,只想通过XML配置(可能是在旧项目中使用disconf)来实现分布式配置的需求。因此,disconf亦实现了基于XML分布式的实现方式。 + +#### ReloadablePropertiesFactoryBean实现了配置文件的disconf托管 + +ReloadablePropertiesFactoryBean继承了PropertiesFactoryBean类,它主要做到: + +- 托管配置文件至disconf仓库,并下载至本地。 +- 解析配置数据传递到 ReloadingPropertyPlaceholderConfigurer + +#### ReloadingPropertyPlaceholderConfigurer实现了配置数据至Bean的映射 + +ReloadingPropertyPlaceholderConfigurer继承自Spring的配置类PropertyPlaceholderConfigurer,它会在Spring启动时将配置数据与Bean做映射,以便在检查到配置文件更改时,可以实现Bean相关域值的自动注入。 + +#### ReloadConfigurationMonitor 定时校验配置是否更新 + +它是一个Timer类,定时校验配置是否有更改,进而促发 ReloadingPropertyPlaceholderConfigurer 类来分析要对哪些 Bean实例进行重新注入。 + + +## 系统配置 ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
配置项说明是否必填默认值
conf_server_store_action仓库 URL/api/config
conf_server_zoo_actionzoo URL/api/zoo
conf_server_master_num_action获取远程主机个数的URL/api/getmasterinfo
zookeeper_url_prefixzookeeper的前缀路径名 /disconfserver2
local_dowload_dir下载文件夹, 远程文件下载后会放在这里./disconf/download
+ +## 局限性和注意事项 ## + +[局限性和注意事项](局限性和注意事项.html) + +## 异构系统主备控制实现 + +disconf将会为所有配置提供主备功能的开关,对于一个配置,多台实例机器可以进行竞争成为主机(使用主配置),竞争失败的实例将会成为备机(使用备配置)。基于zookeeper提供的分布式一致性锁,可以非常容易的达到此目的。 + + \ No newline at end of file diff --git "a/docs/source/design/disconf-web\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.md" "b/docs/source/design/disconf-web\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.md" new file mode 100644 index 000000000..bd2d7de17 --- /dev/null +++ "b/docs/source/design/disconf-web\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.md" @@ -0,0 +1,60 @@ +Disconf-web详细设计文档 +======= + +本文档主要阐述了版本 Disconf-Web 的设计。 + +## 详细信息 ## + +https://github.com/knightliao/disconf/tree/master/disconf-web + +## 表结构设计 ## + +配置数据是存储在Mysql里的。 + + config 配置(配置文件或配置项) + config_id 唯一的ID(没有啥意义,主键,自增长而已) + type 配置文件/配置项 + name 配置文件名/配置项KeY名 + value 配置文件:文件的内容,配置项:配置值 + app_id appid + version 版本 + env_id envid + create_time 生成时间 + update_time 修改时间 + + app + app_id APPID(主键,自增长) + name APP名(一般是产品线+服务名) + description 介绍 + create_time 生成时间 + update_time 修改时间 + emails 邮箱列表逗号分隔 + + env (rd/qa/local可以自定义,默认为 DEFAULT_ENV) + env_id 环境ID(主键,自增长) + name 环境名字 + + user + user_id 用户ID(主键,自增长) + name 姓名 + password 密码 + token token + ownapps 能操作的APPID,逗号分隔 + role_id 角色ID + + role + role_id ID(主键,自增长) + role_name 角色名 + create_time 生成时间 + create_by 创建人 + update_time 修改时间 + update_by 更新人 + + role_resource + role_res_id role-resource id(主键,自增长) + role_id 用户角色id + url_pattern controller_requestMapping_value + method_requestMapping_value + url_description url功能描述 + method_mask GET, PUT, POST, DELETE, 1: accessible + update_time 更新时间 + \ No newline at end of file diff --git a/docs/source/design/index.rst b/docs/source/design/index.rst new file mode 100644 index 000000000..b412373ad --- /dev/null +++ b/docs/source/design/index.rst @@ -0,0 +1,13 @@ +Disconf设计 +========= + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/分布式配置管理平台Disconf + src/disconf-client详细设计文档 + src/disconf-web详细设计文档 + src/局限性和注意事项 + src/Zookeeper异常考虑 + src/细节讨论 \ No newline at end of file diff --git "a/docs/source/design/src/Zookeeper\345\274\202\345\270\270\350\200\203\350\231\221.rst" "b/docs/source/design/src/Zookeeper\345\274\202\345\270\270\350\200\203\350\231\221.rst" new file mode 100644 index 000000000..49f8ebd90 --- /dev/null +++ "b/docs/source/design/src/Zookeeper\345\274\202\345\270\270\350\200\203\350\231\221.rst" @@ -0,0 +1,99 @@ +disconf的Zookeeper异常考虑 +========================== + +disconf-web的ZK异常处理 +----------------------- + +disconf-web可以完全保证在任何情况下,与ZK集群的自动连接。 + +下面按情况进行分析: + +服务启动前,zk连接不上: +~~~~~~~~~~~~~~~~~~~~~~~~ + +- 开始连接不上: + + - apache ZK client自身会自动(永久)去连接ZK server. + 但是一直连接不上。 + - 因此,web上所有操作均会失败,抛大异常,请求失败,只会重试一次,不会重试多次 + +- 后面突然连接上了: + + - apache ZK client 收到server SyncConnected消息。 + - 这时所有操作均成功 + +- 后面又突然连接不上了: + + - apache ZK client 收到server Disconnected 消息。 + - 这时,apache ZK client自身会自动(永久)去连接ZK server. + 但是一直连接不上。 + - 这时 web 上 + 所有操作均会失败,抛大异常,请求失败,只会重试一次,不会重试多次 + +- 后面突然连接上了: + + - apache ZK client 收到server Expired 消息。 + - 这时表示会话丢失啦,apache ZK client + 自动断开与Server的连接,表示此时让你来处理,因为它不知道应该如何处理。 + - 这时,disconf-core会reconnect zkserver,重新建立会话。 + - 成功后,apache ZK client 收到server SyncConnected + 消息。表示连接成功 + +- 后面又突然连接不上了: + + - apache ZK client 收到server Disconnected 消息。 + - 这时,apache ZK client自身会自动(永久)去连接ZK server. + 但是一直连接不上。 + - 这时 web 上 + 所有操作均会失败,抛大异常,请求失败,只会重试一次,不会重试多次 + +服务启动前,zk连接上了: +~~~~~~~~~~~~~~~~~~~~~~~~ + +- 开始连接: + + - apache ZK client 收到server SyncConnected消息。 + - 这时所有操作均成功功 + +- 后面又突然连接不上了…… (与上面分析一样,此不再赘述) + +注意 +~~~~ + +ZK一般需要以集群的形式提供出来。假设有N台ZK, + +- 只要至少有一台ZK存活,disconf-web就可以正常工作。而且永远不会收到 + server Expired 的消息。 +- 只要有一台ZK死亡,disconf-web就会收到 Disconnected + 消息。但是系统仍可以继续工作。 +- 如果所有zk都死亡,那么disconf-web会收到 Disconnected + 消息。只要有一台存活,disconf-web就会收到 + +disconf-client的ZK异常处理 +-------------------------- + +disconf-client可以完全保证: +**如果在启动程序时保证ZK集群是可用的**\ ,那么,就可以保证在任何情况下,与ZK集群的自动连接。 + +下面按情况进行分析: + +程序启动前,zk连接不上: +~~~~~~~~~~~~~~~~~~~~~~~~ + +这时disconf-client无法在ZK上注册信息。这是必须禁止发生的情况。也是disconf-client无法支持的情况。 + +一旦发生这种情况,请先恢复ZK集群,再启动你的程序。 + +程序启动前,zk连接上了: +~~~~~~~~~~~~~~~~~~~~~~~~ + +如果在程序启动过程中,ZK是正常的,那么,disconf-client可以保证与ZK连接的自动性。 + +- 只要集群有一台还存活着,你的程序配置还是受disconf托管。 +- 如果集群所有机器均死亡,这时你的程序将游离于disconf之外。只要集群中有任何一台ZK机器重新开启,那么 + 你的程序将重新 由disconf进行托管。 + +注意 +~~~~ + +disconf-client必须保证在程序在启动时,ZK集群的可用性。 diff --git "a/docs/source/design/src/disconf-client\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.rst" "b/docs/source/design/src/disconf-client\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.rst" new file mode 100644 index 000000000..e8af69234 --- /dev/null +++ "b/docs/source/design/src/disconf-client\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.rst" @@ -0,0 +1,607 @@ +Disconf-client详细设计文档 +========================== + +本文档主要阐述了版本 Disconf-Client 的设计。、 + +程序运行流程图 +-------------- + +版本2.0的设计 +~~~~~~~~~~~~~ + +|image0| + +`点击查看大图 `__ + +**运行流程详细介绍:** + +- **启动事件A**\ :以下按顺序发生。 + + - A1:扫描静态注解类数据,并注入到配置仓库里。 + - A2:根据仓库里的配置文件、配置项,到 disconf-web + 平台里下载配置数据。 + - A3:将下载得到的配置数据值注入到仓库里。 + - A4:根据仓库里的配置文件、配置项,去ZK上监控结点。 + - A5:根据XML配置定义,到 disconf-web + 平台里下载配置文件,放在仓库里,并监控ZK结点。 + - A6:A1-A5均是处理静态类数据。A6是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。 + +- **更新配置事件B**\ :以下按顺序发生。 + + - B1:管理员在 Disconf-web 平台上更新配置。 + - B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。 + - B3:ZK通知 Disconf-cient 模块。 + - B4:与A2一样。唯一不同的是它只处理一个配置文件或者一个配置项,而事件A2则是处理所有配置文件和配置项。下同。 + - B5:与A3一样。 + - B6:基本与A4一样,区别是,这里还会将配置的新值注入到配置实体里。 + +完全版的设计 +~~~~~~~~~~~~ + +|image1| + +[点击查看大图 +](\ http://ww3.sinaimg.cn/mw1024/60c9620fjw1eqj81no7shj20l50h2q65.jpg + +**运行流程详细介绍:** + +与2.0版本的主要区别是支持了:主备分配功能/主备切换事件。 + +- **启动事件A**\ :以下按顺序发生。 + + - A3:扫描静态注解类数据,并注入到配置仓库里。 + - A4+A2:根据仓库里的配置文件、配置项,去 disconf-web + 平台里下载配置数据。这里会有主备竞争 + - A5:将下载得到的配置数据值注入到仓库里。 + - A6:根据仓库里的配置文件、配置项,去ZK上监控结点。 + - A7+A2:根据XML配置定义,到 disconf-web + 平台里下载配置文件,放在仓库里,并监控ZK结点。这里会有主备竞争。 + - A8:A1-A6均是处理静态类数据。A7是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。 + +- **更新配置事件B**\ :以下按顺序发生。 + + - B1:管理员在 Disconf-web 平台上更新配置。 + - B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。 + - B3:ZK通知 Disconf-cient 模块。 + - B4:与A4一样。 + - B5:与A5一样。 + - B6:基本与A4一样,唯一的区别是,这里还会将配置的新值注入到配置实体里。 + +- **主备机切换事件C**\ :以下按顺序发生。 + + - C1:发生主机挂机事件。 + - C2:ZK通知所有被影响到的备机。 + - C4:与A2一样。 + - C5:与A4一样。 + - C6:与A5一样。 + - C7:与A6一样。 + +类设计图 +-------- + +|image2| + +`查看大图 `__ + +**Disconf-client包括的大模块有:** + +- scan 配置扫描模块 +- core 配置核心处理模块 +- fetch 配置抓取模块 +- watch 配置监控模块 +- store 配置仓库模块 +- addons 配置reload模块 + +**各个模块均采用以下设计模式来进设计:** + +- 各个模块均以接口的方式对外暴露,松耦合,强内聚 +- 各个模块均提供工厂类由其它模块来进行获取实例,实例的操纵方式均采用接口方式。 +- 对于配置文件和配置项,采用类扩展的方法来避免if else判断。 + +Disconf-client 的启动 +--------------------- + +启动分成两步,由两个Bean来实现 + +:: + + + + + + + +这里 com.baidu.disconf.dem 是要扫描的类。 + +第一步由Bean com.baidu.disconf.client.DisconfMgrBean 来控制。第二步由 +com.baidu.disconf.client.DisconfMgrBeanSecond 控制。 + +第一步:com.baidu.disconf.client.DisconfMgrBean +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +此Bean实现了BeanFactoryPostProcessor和PriorityOrdered接口。它的Bean初始化Order是最高优先级的。 + +因此,当Spring扫描了所有的Bean信息后,在所有Bean初始化(init)之前,DisconfMgrBean的postProcessBeanFactory方法将被调用,在这里,Disconf-Client会进行第一次扫描。 + +扫描按顺序做了以下几个事情: + +#. 初始化Disconf-client自己的配置模块。 +#. 初始化Scan模块。 +#. 初始化Core模块,并极联初始化Watch,Fetcher,Restful模块。 +#. 扫描用户类,整合分布式配置注解相关的静态类信息至配置仓库里。 +#. 执行Core模块,从disconf-web平台上下载配置数据:配置文件下载到本地,配置项直接下载。 +#. 配置文件和配置项的数据会注入到配置仓库里。 +#. 使用watch模块为所有配置关联ZK上的结点。 + +其中对配置的处理详细为: + +|image3| + +`查看大图 `__ + +第二步:com.baidu.disconf.client.DisconfMgrBeanSecond +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +DisconfMgrBean的扫描主要是静态数据的初始化,并未涉及到动态数据。DisconfMgrBeanSecond +Bean则是将一些动态的数据写到仓库里。 + +本次扫描按顺序做了以下几个事情: + +#. 将配置更新回调实例放到配置仓库里 +#. 为配置实例注入值。 + +|image4| + +`查看大图 `__ + +分布式配置的实现 +---------------- + +下面将 分别详细阐述 分布式配置文件 和 分布式配置项 的实现方式。 + +由于目前版本只支持 +Spring编程方式,因此,以下均只阐述Spring编程下的实现方式。 + +注解式实现 +~~~~~~~~~~ + +分布式配置文件的实现 +^^^^^^^^^^^^^^^^^^^^ + +**定义分布式配置文件类** + +对于配置文件,我们必须实现一个Java类来表示此 分布式配置文件。如: + +:: + + package com.example.disconf.demo.config; + + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @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; + } + } + +对于此Java类,它必须是Spring托管的。此配置文件是redis.properties。 + +此配置类必须标注为 +@DisconfFile,标识它是一个分布式配置文件。且必须指定文件名。 + +此配置类含有两个配置项,分别是host和port。这两个变量必须有 get +方法。且get方法名必须是符合JavaBean规范的。 + +我们通过在这两个变量的 get 方法上添加 @DisconfFileItem +注解来标注它是分布式配置文件里的配置项。必须指定name参数,表示配置文件里的KEY值。associateField值是可选的,表示此get方法相对应的域的名字。 + +**Disconf-client优先启动,并从平台上下载配置文件:** + +应用程序启动时,当Spring容器扫描了所有Java +Bean却还未初始化这些Bean时,disconf-client +模块会优先开始初始化(最高优先级)。它会将 +配置文件名、配置项名记录在配置仓库里,并去 disconf-web +平台下载配置文件至classpath目录下。并且,还会到ZK上生成相应的结点。 + +接着Spring开始初始化用户定义的SpringBean。由于配置文件已经被正确下载至Classpath路径下,因此,JavaBean的配置文件使用的是分布式配置文件,而非本地的配置文件。 + +**待SpringBean初始化后,Disconf-client会获取配置更新回调类实例:** + +此时,Spring上的所有Bean均已被init。Disconf-client模块会再次运行,这时它会去获取用户撰写的配置更新回调函数类实例。 + +一个配置更新回调函数通常是这样撰写的: + +:: + + package com.example.disconf.demo.service.callbacks; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfUpdateService; + import com.baidu.disconf.client.common.update.IDisconfUpdate; + import com.example.disconf.demo.config.Coefficients; + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.service.SimpleRedisService; + + /** + * 更新Redis配置时的回调函数 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + @DisconfUpdateService(classes = {JedisConfig.class}, itemKeys = {Coefficients.key}) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisServiceUpdateCallback.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + /** + * + */ + public void reload() throws Exception { + + simpleRedisService.changeJedis(); + } + + } + +此类必须实现接口IDisconfUpdate,它可以不必是Java托管的。如果是SpringBean,则disconf-client会从Spring容器里获取此Bean。如果它不是SpringBean,disconf-client就会new一个实例出来。 + +使用SpringBean来定义此类的好处是,\ `我们可以在此类中使用@Autowired来使用其它SpringBean `__\ 。比较方便些。 + +`disconf-client根据注解@DisconfUpdateService `__ +以配置文件为Key,将回调函数实例列表放在此Key的Map里。当配置文件更新时,这些回调函数实例就会被按顺序执行。 + +**配置文件更新时,分布式配置文件会重新被下载:** + +当配置文件更新时,disconf-client便会重新从 disconf-web +平台下载配置文件,并重新将值放在配置仓库里。并按顺序进行调用回调函数类的 +reload() 方法。 + +**如何使用分布式配置文件类:** + +在上面我们说到,配置文件类中的配置项必须有 get 方法,并且必须有 +@DisconfFileItem 注解。 + +在 get 上面添加注解的原因就是为了做切面。 + +disconf-cient使用Spring AOP拦截 +`系统里所有含有@DisconfFileItem注解的 `__ +get 方法,把所有此类请求都定向到用户程序的配置仓库中去获取。 + +通过这种方式,我们可以实现统一的、集中式的在配置仓库里去获取配置文件数据。这是一种简洁的实现方式。 + +分布式配置项的实现 +^^^^^^^^^^^^^^^^^^ + +配置项相对于配置文件,比较灵活。我们可以在任何SpringBean里添加配置项。 + +如以下是在一个配置文件类里添加配置项: + +:: + + package com.example.disconf.demo.config; + + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + import com.baidu.disconf.client.common.annotations.DisconfItem; + + /** + * 金融系数文件 + */ + @Service + @DisconfFile(filename = "coefficients.properties") + public class Coefficients { + + public static final String key = "discountRate"; + + @Value(value = "2.0d") + private Double discount; + + private double baiFaCoe; + + private double yuErBaoCoe; + + /** + * 阿里余额宝的系数, 分布式文件配置 + * + * @return + */ + @DisconfFileItem(name = "coe.baiFaCoe") + public double getBaiFaCoe() { + return baiFaCoe; + } + + public void setBaiFaCoe(double baiFaCoe) { + this.baiFaCoe = baiFaCoe; + } + + /** + * 百发的系数, 分布式文件配置 + * + * @return + */ + @DisconfFileItem(name = "coe.yuErBaoCoe") + public double getYuErBaoCoe() { + return yuErBaoCoe; + } + + public void setYuErBaoCoe(double yuErBaoCoe) { + this.yuErBaoCoe = yuErBaoCoe; + } + + /** + * 折扣率,分布式配置 + * + * @return + */ + @DisconfItem(key = key) + public Double getDiscount() { + return discount; + } + + public void setDiscount(Double discount) { + this.discount = discount; + } + } + +或者,我们也可以在一个Service类里添加配置项: + +:: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.Coefficients; + + /** + * 金融宝服务,计算一天赚多少钱 + * + * @author liaoqiqi + * @version 2014-5-16 + */ + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory.getLogger(BaoBaoService.class); + + public static final String key = "moneyInvest"; + + @Value(value = "2000d") + private Double moneyInvest; + + @Autowired + private Coefficients coefficients; + + /** + * 计算百发一天赚多少钱 + * + * @return + */ + public double calcBaiFa() { + return coefficients.getBaiFaCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * k 计算余额宝一天赚多少钱 + * + * @return + */ + public double calcYuErBao() { + return coefficients.getYuErBaoCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * 投资的钱,分布式配置
+ *
+ * 这里切面无法生效,因为SpringAOP不支持。
+ * 但是这里还是正确的,因为我们会将值注入到Bean的值里. + * + * @return + */ + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + + public void setMoneyInvest(Double moneyInvest) { + this.moneyInvest = moneyInvest; + } + } + +采用哪种方式,由用户选择。 + +值得注意的是,在第二种实现中,它的方法calcBaiFa() 时调用了 +getMoneyInvest() 方法。 getMoneyInvest() +是配置项的get方法,\ `它添加了@DisconfItem注解 `__\ ,表明它是一个配置项,并且会被切面拦截,moneyInvest的值会在配置仓库里获取。但是,可惜的是,SpringAOP是无法拦截"Call +myself"方法的。也就是说getMoneyInvest()是无法被切面拦截到的。 + +为了解决此问题,在实现中,我们不仅将它的值 +注入到配置仓库中,而且还注入到配置项所在类的实例里。因此,在上面第二种实现中,虽然 +getMoneyInvest() 方法无法被拦截,但是它返回的还是正确的分布式值的。 + +配置文件也一样,配置值亦会注入到配置文件类实体中。 + +非Spring编程的实现 +^^^^^^^^^^^^^^^^^^ + +在非Spring方式下,无法使用AOP切面编程,因此无法统一的拦截配置数据请求。 + +在这种情况下,用户配置类的实现有两种方式: + +#. 配置类的域是static。用户直接访问这些域便可以获取得到配置类数据。 +#. 配置类使用单例。用户通过单例访问配置获取配置类数据。 + +注意:此两种方式均无法自动避免“配置读取不一致问题”。 + +当事件发生时,用户程序处理配置的方式是: + +#. 配置文件更新时,系统会自动去下载配置文件存储到本地,并存储到配置仓库。对于static变量,系统会自动注入到配置类中。对于使用单例实现方式,用户必须在回调函数中进行用户配置类的更新。 +#. 配置项更新时,与配置文件更新一样。 + +Zookeeper的目录存储结构 +^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + |----disconf + |----app1_version1_env1 + |----file + |----confA.properties + |----item + |----keyA + |----app2_version2_env2 + |----file + |----conf2.properties + |----item + |----key2 + +基于XML的实现 +~~~~~~~~~~~~~ + +| 虽然注解式编程简单、直观,易维护,但是,它是具有一定的代码侵入性的。 +| disconf考虑到有些用户不想写代码,只想通过XML配置(可能是在旧项目中使用disconf)来实现分布式配置的需求。因此,disconf亦实现了基于XML分布式的实现方式。 + +ReloadablePropertiesFactoryBean实现了配置文件的disconf托管 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ReloadablePropertiesFactoryBean继承了PropertiesFactoryBean类,它主要做到: + +- 托管配置文件至disconf仓库,并下载至本地。 +- 解析配置数据传递到 ReloadingPropertyPlaceholderConfigurer + +ReloadingPropertyPlaceholderConfigurer实现了配置数据至Bean的映射 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ReloadingPropertyPlaceholderConfigurer继承自Spring的配置类PropertyPlaceholderConfigurer,它会在Spring启动时将配置数据与Bean做映射,以便在检查到配置文件更改时,可以实现Bean相关域值的自动注入。 + +ReloadConfigurationMonitor 定时校验配置是否更新 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +它是一个Timer类,定时校验配置是否有更改,进而促发 +ReloadingPropertyPlaceholderConfigurer 类来分析要对哪些 +Bean实例进行重新注入。 + +系统配置 +-------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
配置项说明是否必填默认值
conf_server_store_action仓库 URL/api/config
conf_server_zoo_actionzoo URL/api/zoo
conf_server_master_num_action获取远程主机个数的URL/api/getmasterinfo
zookeeper_url_prefixzookeeper的前缀路径名 /disconfserver2
local_dowload_dir下载文件夹, 远程文件下载后会放在这里./disconf/download
+ +局限性和注意事项 +---------------- + +`局限性和注意事项 <局限性和注意事项.html>`__ + +异构系统主备控制实现 +-------------------- + +disconf将会为所有配置提供主备功能的开关,对于一个配置,多台实例机器可以进行竞争成为主机(使用主配置),竞争失败的实例将会成为备机(使用备配置)。基于zookeeper提供的分布式一致性锁,可以非常容易的达到此目的。 + +.. |image0| image:: http://ww3.sinaimg.cn/bmiddle/60c9620fjw1eqi7tnuic8j20l50g7acs.jpg +.. |image1| image:: http://ww3.sinaimg.cn/bmiddle/60c9620fjw1eqj81no7shj20l50h2q65.jpg +.. |image2| image:: http://ww4.sinaimg.cn/bmiddle/60c9620fgw1ej0ycv2fjbj21ao0u8441.jpg +.. |image3| image:: http://ww3.sinaimg.cn/bmiddle/60c9620fgw1ej1x5tfvzgj20pj141421.jpg +.. |image4| image:: http://ww3.sinaimg.cn/bmiddle/60c9620fgw1ej1x5ve5w6j20pj11xwj4.jpg + diff --git "a/docs/source/design/src/disconf-web\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.rst" "b/docs/source/design/src/disconf-web\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.rst" new file mode 100644 index 000000000..296d0b071 --- /dev/null +++ "b/docs/source/design/src/disconf-web\350\257\246\347\273\206\350\256\276\350\256\241\346\226\207\346\241\243.rst" @@ -0,0 +1,64 @@ +Disconf-web详细设计文档 +======================= + +本文档主要阐述了版本 Disconf-Web 的设计。 + +详细信息 +-------- + +https://github.com/knightliao/disconf/tree/master/disconf-web + +表结构设计 +---------- + +配置数据是存储在Mysql里的。 + +:: + + config 配置(配置文件或配置项) + config_id 唯一的ID(没有啥意义,主键,自增长而已) + type 配置文件/配置项 + name 配置文件名/配置项KeY名 + value 配置文件:文件的内容,配置项:配置值 + app_id appid + version 版本 + env_id envid + create_time 生成时间 + update_time 修改时间 + + app + app_id APPID(主键,自增长) + name APP名(一般是产品线+服务名) + description 介绍 + create_time 生成时间 + update_time 修改时间 + emails 邮箱列表逗号分隔 + + env (rd/qa/local可以自定义,默认为 DEFAULT_ENV) + env_id 环境ID(主键,自增长) + name 环境名字 + + user + user_id 用户ID(主键,自增长) + name 姓名 + password 密码 + token token + ownapps 能操作的APPID,逗号分隔 + role_id 角色ID + + role + role_id ID(主键,自增长) + role_name 角色名 + create_time 生成时间 + create_by 创建人 + update_time 修改时间 + update_by 更新人 + + role_resource + role_res_id role-resource id(主键,自增长) + role_id 用户角色id + url_pattern controller_requestMapping_value + method_requestMapping_value + url_description url功能描述 + method_mask GET, PUT, POST, DELETE, 1: accessible + update_time 更新时间 + diff --git "a/docs/source/design/src/\345\210\206\345\270\203\345\274\217\351\205\215\347\275\256\347\256\241\347\220\206\345\271\263\345\217\260Disconf.rst" "b/docs/source/design/src/\345\210\206\345\270\203\345\274\217\351\205\215\347\275\256\347\256\241\347\220\206\345\271\263\345\217\260Disconf.rst" new file mode 100644 index 000000000..40b401bed --- /dev/null +++ "b/docs/source/design/src/\345\210\206\345\270\203\345\274\217\351\205\215\347\275\256\347\256\241\347\220\206\345\271\263\345\217\260Disconf.rst" @@ -0,0 +1,294 @@ +分布式配置管理平台Disconf +========================= + +摘要 +---- + +为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案(简称为disconf[4],下同)。首先,实现了同构系统的配置发布统一化,提供了配置服务server,该服务可以对配置进行持久化管理并对外提供restful接口,在此基础上,基于zookeeper实现对配置更改的实时推送,并且,提供了稳定有效的容灾方案,以及用户体验良好的编程模型和WEB用户管理界面。其次,实现了异构系统的配置包管理,提出基于zookeeper的全局分布式一致性锁来实现主备统一部署、系统异常时的主备自主切换。通过在百度内部以及外部等多个产品线的实践结果表明,本解决方案是有效且稳定的。 + +技术背景 +-------- + +在一个分布式环境中,同类型的服务往往会部署很多实例。这些实例使用了一些配置,为了更好地维护这些配置就产生了配置管理服务。通过这个服务可以轻松地管理成千上百个服务实例的配置问题。 + +王阿晶提出了基于zooKeeper的配置信息存储方案的设计与实现[1], +它将所有配置存储在zookeeper上,这会导致配置的管理不那么方便,而且他们没有相关的源码实现。淘宝的diamond[2]是淘宝内部使用的一个管理持久配置的系统,它具有完整的开源源码实现,它的特点是简单、可靠、易用,淘宝内部绝大多数系统的配置都采用diamond来进行统一管理。他将所有配置文件里的配置打散化进行存储,只支持KV结构,并且配置更新的推送是非实时的。百度内部的BJF配置中心服务[3]采用了类似淘宝diamond的实现,也是配置打散化、只支持KV和非实时推送。 + +同构系统是市场的主流,特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多。但是,异构系统也有一定的存在意义,譬如,对于“拉模式”的多个下游实例,同一时间点只能只有一个下游实例在运行。在这种情景下,就存在多台实例机器有“主备机”模式的问题。目前国内并没有很明显的解决方案来统一解决此问题。 + +功能特点与设计理念 +------------------ + +disconf是一套完整的基于zookeeper的分布式配置统一解决方案。 + +**它的功能特点是** + +- 支持配置(配置项+配置文件)的分布式化管理 + + - 配置发布统一化 + - 配置发布、更新统一化(云端存储、发布):配置存储在云端系统,用户统一在平台上进行发布、更新配置。 + - 配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。特殊地,如果用户为此配置定义了回调函数类,则此函数类会被自动调用。 + +- 配置异构系统管理 + + - 异构包部署统一化:这里的异构系统是指一个系统部署多个实例时,由于配置不同,从而需要多个部署包(jar或war)的情况(下同)。使用Disconf后,异构系统的部署只需要一个部署包,不同实例的配置会自动分配。特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多,Disconf可以很自然地与他天然契合。 + 异构主备自动切换:如果一个异构系统存在主备机,主机发生挂机时,备机可以自动获取主机配置从而变成主机。 + - 异构主备机Context共享工具:异构系统下,主备机切换时可能需要共享Context。可以使用Context共享工具来共享主备的Context。 + +- 注解式编程,极简的使用方式:我们追求的是极简的、用户编程体验良好的编程方式。通过简单的标注+极简单的代码撰写,即可完成复杂的配置分布式化。 +- 需要Spring编程环境 + +**它的设计理念是:** + +- 简单,用户体验良好: + + - 摒弃了打散化配置的管理方式[2,3],仍旧采用基于配置文件的编程方式,这和程序员以前的编程习惯(配置都是放在配置文件里)一致。特别的,为了支持较为小众的打散化配置功能,还特别支持了配置项。 + - 采用了基于XML无代码侵入编程方式:只需要几行XML配置,即可实现配置文件发布更新统一化、自动化。 + - 采用了基于注解式的弱代码侵入编程方式:通过编程规范,一个配置文件一个配置类,代码结构简单易懂。XML几乎没有任何更改,与原springXML配置一样。真正编程时,几乎感觉不到配置已经分布式化 + +- 可以托管任何类型的配置文件,这与[2,3]只能支持KV结构的功能有较大的改进。 +- 配置更新实时推送 +- 提供界面良好Web管理功能,可以非常方便的查看配置被哪些实例使用了。 + +详细设计 +-------- + +架构设计 +~~~~~~~~ + +**disconf服务集群模式**\ : + +|image0| + +**disconf的模块架构图**\ : + +|image1| + +每个模块的简单介绍如下: + +- Disconf-core + + - 分布式通知模块:支持配置更新的实时化通知 + - 路径管理模块:统一管理内部配置路径URL + +- Disconf-client + + - 配置仓库容器模块:统一管理用户实例中本地配置文件和配置项的内存数据存储 + - 配置reload模块:监控本地配置文件的变动,并自动reload到指定bean + - 扫描模块:支持扫描所有disconf注解的类和域 + - 下载模块:restful风格的下载配置文件和配置项 + - watch模块:监控远程配置文件和配置项的变化 + - 主备分配模块:主备竞争结束后,统一管理主备分配与主备监控控制 + - 主备竞争模块:支持分布式环境下的主备竞争 + +- Disconf-web + + - 配置存储模块:管理所有配置的存储和读取 + - 配置管理模块:支持配置的上传、下载、更新 + - 通知模块:当配置更新后,实时通知使用这些配置的所有实例 + - 配置自检监控模块:自动定时校验实例本地配置与中心配置是否一致 + - 权限控制:web的简单权限控制 + +- Disconf-tools + + - context共享模块:提供多实例间context的共享。 + +流程设计 +~~~~~~~~ + +|image2| + +**运行流程详细介绍:** + +与2.0版本的主要区别是支持了:主备分配功能/主备切换事件。 + +- **启动事件A**\ :以下按顺序发生。 + + - A3:扫描静态注解类数据,并注入到配置仓库里。 + - A4+A2:根据仓库里的配置文件、配置项,去 disconf-web + 平台里下载配置数据。这里会有主备竞争 + - A5:将下载得到的配置数据值注入到仓库里。 + - A6:根据仓库里的配置文件、配置项,去ZK上监控结点。 + - A7+A2:根据XML配置定义,到 disconf-web + 平台里下载配置文件,放在仓库里,并监控ZK结点。这里会有主备竞争。 + - A8:A1-A6均是处理静态类数据。A7是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。 + +- **更新配置事件B**\ :以下按顺序发生。 + + - B1:管理员在 Disconf-web 平台上更新配置。 + - B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。 + - B3:ZK通知 Disconf-cient 模块。 + - B4:与A4一样。 + - B5:与A5一样。 + - B6:基本与A4一样,唯一的区别是,这里还会将配置的新值注入到配置实体里。 + +- **主备机切换事件C**\ :以下按顺序发生。 + + - C1:发生主机挂机事件。 + - C2:ZK通知所有被影响到的备机。 + - C4:与A2一样。 + - C5:与A4一样。 + - C6:与A5一样。 + - C7:与A6一样。 + +模块实现 +~~~~~~~~ + +| disconf-web提供了前后端分离的web架构,具体可见: +| https://github.com/knightliao/disconf/tree/master/disconf-web + +本部分会重点介绍disconf-client的实现方式。 + +注解式disconf实现 +^^^^^^^^^^^^^^^^^ + +本实现会涉及到 配置仓库容器模块、扫描模块、下载模块、watch模块, + +|http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg| + +使用AOP拦截的一个好处是可以比较轻松的实现配置控制,比如并发环境下的配置统一生效。关于这方面的讨论可以见\ `这里 `__\ 。 + +特别地,本方式提供的编程模式非常简单,例如使用以下配置类的程序在使用它时,\ `可以直接@Autowired进来进行调用 `__\ ,使用它时就和平常使用普通的JavaBean一样,但其实它已经分布式化了。配置更新时,配置类亦会自动更新。 + +:: + + @Service + @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; + } + } + +基于XML配置disconf实现 +^^^^^^^^^^^^^^^^^^^^^^ + +本实现提供了无任何代码侵入方式的分布式配置。 + +ReloadablePropertiesFactoryBean继承了Spring +Properties文件的PropertiesFactoryBean类,管理所有当配置更新时要进行reload的配置文件。对于被管理的每一个配置文件,都会通过 +配置仓库容器模块、扫描模块、下载模块、watch模块 +进行配置获取至配置仓库里。 + +ReloadingPropertyPlaceholderConfigurer继承了Spring +Bean配置值控制类PropertyPlaceholderConfigurer。在第一次扫描spring bean +时,disconf会记录配置文件的配置与哪些bean有关联。 + +ReloadConfigurationMonitor是一个定时任务,定时check本地配置文件是否有更新。 + +当配置中心的配置被更新时,配置文件会被下载至实例本地,ReloadConfigurationMonitor即会监控到此行为,并且通知 +ReloadingPropertyPlaceholderConfigurer 对相关的bean类进行值更新。 + +特别的,此种方式无法解决并发情况下配置统一生效的问题。 + +主备分配实现 +^^^^^^^^^^^^ + +在实现中,为每个配置提供主备选择的概念。用户实例在获取配置前需要先进行全局唯一性竞争才能得到配置值。在这里,我们采用基于zookeeper的全局唯一性锁来实现。 + +Comparisons +----------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
淘宝Diamond[2]Disconf比较
数据持久性存储在mysql上存储在mysql上都持久化到数据库里,都易于管理
推拉模型拉模型,每隔15s拉一次全量数据基于Zookeeper的推模型,实时推送disconf基于分布式的Zookeeper来实时推送,不断是在稳定性、实效性、易用性上均优于diamond
配置读写支持实例对配置读写。支持某台实例写配置数据,并广播到其它实例上只支持实例对配置读。通过在disconf-web上更新配置到达到广播写到所有应用实例从目前的应用场景来看,实例对配置的写需求不是那么明显。disconf支持的中心化广播方案可能会与人性思考更加相似。
容灾多级容灾模式,配置数据会dump在本地,避免中心服务挂机时无法使用多级容灾模式,优先读取本地配置文件。双方均支持在中心服务挂机时配置实例仍然可以使用
配置数据模型只支持KV结构的数据,非配置文件模式支持传统的配置文件模式(配置文件),亦支持KV结构数据(配置项)使用配置文件的编程方式可能与程序员的编程习惯更为相似,更易于接受和使用。
编程模型需要将配置文件拆成多个配置项,没有明显的编程模型在使用配置文件的基础上,提供了注解式和基于XML的两种编程模型
并发性多条配置要同时生效时,无法解决并发同时生效的问题基于注解式的配置,可以解决并发性问题
+ +Reference +--------- + +#. 王阿晶,邹仕洪: + `基于ZooKeeper的配置信息存储方案的设计与实现 `__ +#. 淘宝diamod实现:\ http://code.taobao.org/p/diamond/src/, 2012 +#. `百度BJF配置中心 `__, + 2014 +#. disconf github: https://github.com/knightliao/disconf, 2014 +#. `淘宝分布式配置管理服务Diamond `__ +#. `zooKeeper和Diamond有什么不同 `__ +#. `diamond专题(一)-- + 简介和快速使用 `__ + +.. |image0| image:: http://ww1.sinaimg.cn/bmiddle/60c9620fgw1ehi7wwkdtoj20nw0fz0uh.jpg +.. |image1| image:: http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqi7cnhjp0j20e4097wfq.jpg +.. |image2| image:: http://ww3.sinaimg.cn/bmiddle/60c9620fjw1eqj81no7shj20l50h2q65.jpg +.. |http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg| image:: http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg + diff --git "a/docs/source/design/src/\345\261\200\351\231\220\346\200\247\345\222\214\346\263\250\346\204\217\344\272\213\351\241\271.rst" "b/docs/source/design/src/\345\261\200\351\231\220\346\200\247\345\222\214\346\263\250\346\204\217\344\272\213\351\241\271.rst" new file mode 100644 index 000000000..b6ed49185 --- /dev/null +++ "b/docs/source/design/src/\345\261\200\351\231\220\346\200\247\345\222\214\346\263\250\346\204\217\344\272\213\351\241\271.rst" @@ -0,0 +1,14 @@ +局限性和注意事项 +================ + +局限性和注意事项 +---------------- + +- 配置文件类、配置项所在的类、回调函数类 + 都必须是JavaBean,并且它们的"scope" + 都必须是\ `singleton `__\ 的。 +- 本系统实现的注解方案具有些局限性,具体如下: + + - 用户标注配置时略有些不习惯。目前注解是放在get方法之上的,而不是放在域上。 + - 注解放在get方法上,一般情况下是没有问题的。但是对于"call + self"的方法调用,AOP无法拦截得到,这样就无法统一处理这些配置。一旦出现这种情况,“非一致性读问题”就会产生。 diff --git "a/docs/source/design/src/\347\273\206\350\212\202\350\256\250\350\256\272.rst" "b/docs/source/design/src/\347\273\206\350\212\202\350\256\250\350\256\272.rst" new file mode 100644 index 000000000..3bca77b35 --- /dev/null +++ "b/docs/source/design/src/\347\273\206\350\212\202\350\256\250\350\256\272.rst" @@ -0,0 +1,84 @@ +细节讨论 +======== + +解决 配置“不一致性读“ 问题 +-------------------------- + +问题描述: +~~~~~~~~~~ + +应用系统的配置更新过程,它会涉及到多个配置项的更新,它不是一个原子过程。如果在配置更新的过程中,应用程序去读取配置,这里可能存在些“时间窗口”,从而导致不一致性读问题。 + +解决方法: +~~~~~~~~~~ + +前提:无论何种实现,要实现统一读取,避免“非一致性”问题,就必须要对所有读取操作“统一化”。 + +Disconf支持Web或非Web系统,对于这个问题,Web系统或非Web系统需要区分来看: + +**对于Web系统:** + +要实现统一读取,可以使用ThreadContext+AOP来实现。 + +AOP的使用:通过对配置的get方法做切面,统一将用户的配置请求转发至 +“我们自己的配置仓库” 里获取,从而实现统一读取。 + +ThreadContext的使用方式有以下几种: + +- 解决方法一:提供ThreadContext包,在每次请求一开始时都复制系统里的所有配置缓存(复制过程要与配置更新Sync互斥),从而保证每次会话的数据的一致性。 +- 解决方法二:提供ThreadContext包,每次请求都绑定一个版本号,如果读取时版本号不一致则报错,需要重新请求。 +- 解决方法三:方法二的加强版,添加一个注解定义,标注它是需要强一制性的,每次会话读取时只复制这些强一制性配置(复制过程要与配置更新Sync互斥)。 +- 解决方法四:提供ThreadContext包,系统内保存有多个配置缓存层,读取时统一读取某个版本的缓存。每当配置更新时,缓存层增加。 + +第一种方法,代价太大。第二种方法,严重增加用户负担,第三种还是需要用户关心这个事情。我们将采用第四种方法。 + +**对于非Web项目:** + +比较难解决非一致性读取的问题。因为它没有了会话这样一个概念。Apache的FileChangedReloadingStrategy +Reload配置文件的方案也没有解决此问题。所以,我们打算放弃这方面的解决。但是,我们还是会提供一个简单却Ugly的解决方案:提供函数来标识用户读取配置的边界。用户可以放弃使用这个方案,但是我们不保证不会发生“不一致读'问题。 + +配置放在哪里 +------------ + +关于配置应该放在哪里?有许多讨论,可以放在Web平台上,也可以放在Zookeeper上。从Disconf的实现中,可以看到Disconf是将配置数据放在Web平台的,而不是放在Zookeeper上的。那为什么这么设计呢?下面有个表格比较一下优劣。 + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
配置放在ZK上配置放在Disconf-web平台上比较
配置管理不易管理。ZK相比Mysql不易管理。用Mysql统一存储所有配置数据,非常方便管理,可扩展性强。配置放在Disconf-web显然容易管理和扩展。
职责分配ZK负责通知与配置数据存储,并提供给client获取数据。disconf-web负责新建、更新配置,并存储一些数据(非配置数据,用作管理),它不为client提供下载配置服务。ZK只负责通知。disconf-web负责新建、更新、存储 配置,并为client提供下载配置服务。如果采用第一种方案,数据分散存储了。第二种方案职责明确,数据统一存储在Disconf-web上,ZK只负责通知。
client的配置获取client启动时,需要从ZK上下载配置。因此必须使用disconf-web先写到ZK上,否则client启动时就无法使用最新配置。配置更新时,client直接从ZK获取最新配置。disconf-web必须统一的在ZK上新建、更新结点。client不管是启动还是更新时,均是从disconf-web上获取配置。启动时,client会在ZK监控结点(如果不存在,则新建);更新时,disconf-web更新ZK结点。disconf-web不会在ZK上新建结点。这两种方案其实都差不多
配置的一致性问题把数据存储在ZK上,无法像Mysql一样可持久化存储。ZK集群关闭后,数据全部丢失。client直接从ZK上获取配置数据,系统运行久之后,client的配置是否正确无从验证。数据存储在Mysql上,可持久化存储。不管未来是迁移或者扩展之类都非常方便。client是从Web上获取数据,然后再写到ZK上。理论上来说,ZK上的数据应该是与Web平台数据一样的,这可以作为验证平台正确性的一个方法。从可持久化性、可验证性方面来讲,第二个方案好。
配置获取配置存储在ZK上,非client想要获取配置,很不容易。配置存储在Web平台上,通过提供RestHttp接口,不管是谁想要获取配置都非常方便。第二种方案获取简单便捷。
diff --git "a/docs/source/design/\345\210\206\345\270\203\345\274\217\351\205\215\347\275\256\347\256\241\347\220\206\345\271\263\345\217\260Disconf.md" "b/docs/source/design/\345\210\206\345\270\203\345\274\217\351\205\215\347\275\256\347\256\241\347\220\206\345\271\263\345\217\260Disconf.md" new file mode 100644 index 000000000..c4e84c19c --- /dev/null +++ "b/docs/source/design/\345\210\206\345\270\203\345\274\217\351\205\215\347\275\256\347\256\241\347\220\206\345\271\263\345\217\260Disconf.md" @@ -0,0 +1,243 @@ +分布式配置管理平台Disconf +======= + +## 摘要 + + 为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案(简称为disconf[4],下同)。首先,实现了同构系统的配置发布统一化,提供了配置服务server,该服务可以对配置进行持久化管理并对外提供restful接口,在此基础上,基于zookeeper实现对配置更改的实时推送,并且,提供了稳定有效的容灾方案,以及用户体验良好的编程模型和WEB用户管理界面。其次,实现了异构系统的配置包管理,提出基于zookeeper的全局分布式一致性锁来实现主备统一部署、系统异常时的主备自主切换。通过在百度内部以及外部等多个产品线的实践结果表明,本解决方案是有效且稳定的。 + +## 技术背景 + +在一个分布式环境中,同类型的服务往往会部署很多实例。这些实例使用了一些配置,为了更好地维护这些配置就产生了配置管理服务。通过这个服务可以轻松地管理成千上百个服务实例的配置问题。 + +王阿晶提出了基于zooKeeper的配置信息存储方案的设计与实现[1], 它将所有配置存储在zookeeper上,这会导致配置的管理不那么方便,而且他们没有相关的源码实现。淘宝的diamond[2]是淘宝内部使用的一个管理持久配置的系统,它具有完整的开源源码实现,它的特点是简单、可靠、易用,淘宝内部绝大多数系统的配置都采用diamond来进行统一管理。他将所有配置文件里的配置打散化进行存储,只支持KV结构,并且配置更新的推送是非实时的。百度内部的BJF配置中心服务[3]采用了类似淘宝diamond的实现,也是配置打散化、只支持KV和非实时推送。 + +同构系统是市场的主流,特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多。但是,异构系统也有一定的存在意义,譬如,对于“拉模式”的多个下游实例,同一时间点只能只有一个下游实例在运行。在这种情景下,就存在多台实例机器有“主备机”模式的问题。目前国内并没有很明显的解决方案来统一解决此问题。 + +## 功能特点与设计理念 + +disconf是一套完整的基于zookeeper的分布式配置统一解决方案。 + +**它的功能特点是** + +- 支持配置(配置项+配置文件)的分布式化管理 + - 配置发布统一化 + - 配置发布、更新统一化(云端存储、发布):配置存储在云端系统,用户统一在平台上进行发布、更新配置。 + - 配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。特殊地,如果用户为此配置定义了回调函数类,则此函数类会被自动调用。 +- 配置异构系统管理 + - 异构包部署统一化:这里的异构系统是指一个系统部署多个实例时,由于配置不同,从而需要多个部署包(jar或war)的情况(下同)。使用Disconf后,异构系统的部署只需要一个部署包,不同实例的配置会自动分配。特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多,Disconf可以很自然地与他天然契合。 +异构主备自动切换:如果一个异构系统存在主备机,主机发生挂机时,备机可以自动获取主机配置从而变成主机。 + - 异构主备机Context共享工具:异构系统下,主备机切换时可能需要共享Context。可以使用Context共享工具来共享主备的Context。 +- 注解式编程,极简的使用方式:我们追求的是极简的、用户编程体验良好的编程方式。通过简单的标注+极简单的代码撰写,即可完成复杂的配置分布式化。 +- 需要Spring编程环境 + +**它的设计理念是:** + +- 简单,用户体验良好: + - 摒弃了打散化配置的管理方式[2,3],仍旧采用基于配置文件的编程方式,这和程序员以前的编程习惯(配置都是放在配置文件里)一致。特别的,为了支持较为小众的打散化配置功能,还特别支持了配置项。 + - 采用了基于XML无代码侵入编程方式:只需要几行XML配置,即可实现配置文件发布更新统一化、自动化。 + - 采用了基于注解式的弱代码侵入编程方式:通过编程规范,一个配置文件一个配置类,代码结构简单易懂。XML几乎没有任何更改,与原springXML配置一样。真正编程时,几乎感觉不到配置已经分布式化 +- 可以托管任何类型的配置文件,这与[2,3]只能支持KV结构的功能有较大的改进。 +- 配置更新实时推送 +- 提供界面良好Web管理功能,可以非常方便的查看配置被哪些实例使用了。 + +## 详细设计 + +### 架构设计 + +**disconf服务集群模式**: + +![](http://ww1.sinaimg.cn/bmiddle/60c9620fgw1ehi7wwkdtoj20nw0fz0uh.jpg) + +**disconf的模块架构图**: + +![](http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqi7cnhjp0j20e4097wfq.jpg) + +每个模块的简单介绍如下: + +- Disconf-core + - 分布式通知模块:支持配置更新的实时化通知 + - 路径管理模块:统一管理内部配置路径URL +- Disconf-client + - 配置仓库容器模块:统一管理用户实例中本地配置文件和配置项的内存数据存储 + - 配置reload模块:监控本地配置文件的变动,并自动reload到指定bean + - 扫描模块:支持扫描所有disconf注解的类和域 + - 下载模块:restful风格的下载配置文件和配置项 + - watch模块:监控远程配置文件和配置项的变化 + - 主备分配模块:主备竞争结束后,统一管理主备分配与主备监控控制 + - 主备竞争模块:支持分布式环境下的主备竞争 +- Disconf-web + - 配置存储模块:管理所有配置的存储和读取 + - 配置管理模块:支持配置的上传、下载、更新 + - 通知模块:当配置更新后,实时通知使用这些配置的所有实例 + - 配置自检监控模块:自动定时校验实例本地配置与中心配置是否一致 + - 权限控制:web的简单权限控制 +- Disconf-tools + - context共享模块:提供多实例间context的共享。 + +### 流程设计 + +![](http://ww3.sinaimg.cn/bmiddle/60c9620fjw1eqj81no7shj20l50h2q65.jpg) + +**运行流程详细介绍:** + +与2.0版本的主要区别是支持了:主备分配功能/主备切换事件。 + +- **启动事件A**:以下按顺序发生。 + - A3:扫描静态注解类数据,并注入到配置仓库里。 + - A4+A2:根据仓库里的配置文件、配置项,去 disconf-web 平台里下载配置数据。这里会有主备竞争 + - A5:将下载得到的配置数据值注入到仓库里。 + - A6:根据仓库里的配置文件、配置项,去ZK上监控结点。 + - A7+A2:根据XML配置定义,到 disconf-web 平台里下载配置文件,放在仓库里,并监控ZK结点。这里会有主备竞争。 + - A8:A1-A6均是处理静态类数据。A7是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。 +- **更新配置事件B**:以下按顺序发生。 + - B1:管理员在 Disconf-web 平台上更新配置。 + - B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。 + - B3:ZK通知 Disconf-cient 模块。 + - B4:与A4一样。 + - B5:与A5一样。 + - B6:基本与A4一样,唯一的区别是,这里还会将配置的新值注入到配置实体里。 +- **主备机切换事件C**:以下按顺序发生。 + - C1:发生主机挂机事件。 + - C2:ZK通知所有被影响到的备机。 + - C4:与A2一样。 + - C5:与A4一样。 + - C6:与A5一样。 + - C7:与A6一样。 + +### 模块实现 + +disconf-web提供了前后端分离的web架构,具体可见: +[https://github.com/knightliao/disconf/tree/master/disconf-web](https://github.com/knightliao/disconf/tree/master/disconf-web) + +本部分会重点介绍disconf-client的实现方式。 + +#### 注解式disconf实现 + +本实现会涉及到 配置仓库容器模块、扫描模块、下载模块、watch模块, + +![http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg](http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg) + +使用AOP拦截的一个好处是可以比较轻松的实现配置控制,比如并发环境下的配置统一生效。关于这方面的讨论可以见[这里](https://github.com/knightliao/disconf/wiki/%E7%BB%86%E8%8A%82%E8%AE%A8%E8%AE%BA)。 + +特别地,本方式提供的编程模式非常简单,例如使用以下配置类的程序在使用它时,可以直接@Autowired进来进行调用,使用它时就和平常使用普通的JavaBean一样,但其实它已经分布式化了。配置更新时,配置类亦会自动更新。 + + @Service + @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; + } + } + +#### 基于XML配置disconf实现 + +本实现提供了无任何代码侵入方式的分布式配置。 + +ReloadablePropertiesFactoryBean继承了Spring Properties文件的PropertiesFactoryBean类,管理所有当配置更新时要进行reload的配置文件。对于被管理的每一个配置文件,都会通过 配置仓库容器模块、扫描模块、下载模块、watch模块 进行配置获取至配置仓库里。 + +ReloadingPropertyPlaceholderConfigurer继承了Spring Bean配置值控制类PropertyPlaceholderConfigurer。在第一次扫描spring bean 时,disconf会记录配置文件的配置与哪些bean有关联。 + +ReloadConfigurationMonitor是一个定时任务,定时check本地配置文件是否有更新。 + +当配置中心的配置被更新时,配置文件会被下载至实例本地,ReloadConfigurationMonitor即会监控到此行为,并且通知 ReloadingPropertyPlaceholderConfigurer 对相关的bean类进行值更新。 + +特别的,此种方式无法解决并发情况下配置统一生效的问题。 + +#### 主备分配实现 + +在实现中,为每个配置提供主备选择的概念。用户实例在获取配置前需要先进行全局唯一性竞争才能得到配置值。在这里,我们采用基于zookeeper的全局唯一性锁来实现。 + +## Comparisons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
淘宝Diamond[2]Disconf比较
数据持久性存储在mysql上存储在mysql上都持久化到数据库里,都易于管理
推拉模型拉模型,每隔15s拉一次全量数据基于Zookeeper的推模型,实时推送disconf基于分布式的Zookeeper来实时推送,不断是在稳定性、实效性、易用性上均优于diamond
配置读写支持实例对配置读写。支持某台实例写配置数据,并广播到其它实例上只支持实例对配置读。通过在disconf-web上更新配置到达到广播写到所有应用实例从目前的应用场景来看,实例对配置的写需求不是那么明显。disconf支持的中心化广播方案可能会与人性思考更加相似。
容灾多级容灾模式,配置数据会dump在本地,避免中心服务挂机时无法使用多级容灾模式,优先读取本地配置文件。双方均支持在中心服务挂机时配置实例仍然可以使用
配置数据模型只支持KV结构的数据,非配置文件模式支持传统的配置文件模式(配置文件),亦支持KV结构数据(配置项)使用配置文件的编程方式可能与程序员的编程习惯更为相似,更易于接受和使用。
编程模型需要将配置文件拆成多个配置项,没有明显的编程模型在使用配置文件的基础上,提供了注解式和基于XML的两种编程模型
并发性多条配置要同时生效时,无法解决并发同时生效的问题基于注解式的配置,可以解决并发性问题
+ +## Reference + +1. 王阿晶,邹仕洪: [基于ZooKeeper的配置信息存储方案的设计与实现](http://wenku.baidu.com/view/ee86ca90daef5ef7ba0d3c7d.html) +2. 淘宝diamod实现:[http://code.taobao.org/p/diamond/src/](http://code.taobao.org/p/diamond/src/), 2012 +3. [百度BJF配置中心](http://wiki.babel.baidu.com/twiki/bin/view/Main/CAP-CC#%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%831.0%E5%BF%AB%E9%80%9F%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97.pptx), 2014 +4. disconf github: [https://github.com/knightliao/disconf](https://github.com/knightliao/disconf), 2014 +5. [淘宝分布式配置管理服务Diamond](http://codemacro.com/2014/10/12/diamond/) +6. [zooKeeper和Diamond有什么不同](http://jm-blog.aliapp.com/?p=2561) +7. [diamond专题(一)-- 简介和快速使用](http://jm-blog.aliapp.com/?p=1588) + diff --git "a/docs/source/design/\345\261\200\351\231\220\346\200\247\345\222\214\346\263\250\346\204\217\344\272\213\351\241\271.md" "b/docs/source/design/\345\261\200\351\231\220\346\200\247\345\222\214\346\263\250\346\204\217\344\272\213\351\241\271.md" new file mode 100644 index 000000000..00d9f8f90 --- /dev/null +++ "b/docs/source/design/\345\261\200\351\231\220\346\200\247\345\222\214\346\263\250\346\204\217\344\272\213\351\241\271.md" @@ -0,0 +1,11 @@ +局限性和注意事项 +======= + +## 局限性和注意事项 ## + +- 配置文件类、配置项所在的类、回调函数类 都必须是JavaBean,并且它们的"scope" 都必须是[singleton](http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html)的。 +- 本系统实现的注解方案具有些局限性,具体如下: + - 用户标注配置时略有些不习惯。目前注解是放在get方法之上的,而不是放在域上。 + - 注解放在get方法上,一般情况下是没有问题的。但是对于"call self"的方法调用,AOP无法拦截得到,这样就无法统一处理这些配置。一旦出现这种情况,“非一致性读问题”就会产生。 + + diff --git "a/docs/source/design/\347\273\206\350\212\202\350\256\250\350\256\272.md" "b/docs/source/design/\347\273\206\350\212\202\350\256\250\350\256\272.md" new file mode 100644 index 000000000..785c81515 --- /dev/null +++ "b/docs/source/design/\347\273\206\350\212\202\350\256\250\350\256\272.md" @@ -0,0 +1,79 @@ +细节讨论 +======= + +## 解决 配置“不一致性读“ 问题 ## + +###问题描述:### + +应用系统的配置更新过程,它会涉及到多个配置项的更新,它不是一个原子过程。如果在配置更新的过程中,应用程序去读取配置,这里可能存在些“时间窗口”,从而导致不一致性读问题。 + +###解决方法:### + +前提:无论何种实现,要实现统一读取,避免“非一致性”问题,就必须要对所有读取操作“统一化”。 + +Disconf支持Web或非Web系统,对于这个问题,Web系统或非Web系统需要区分来看: + +**对于Web系统:** + +要实现统一读取,可以使用ThreadContext+AOP来实现。 + +AOP的使用:通过对配置的get方法做切面,统一将用户的配置请求转发至 “我们自己的配置仓库” 里获取,从而实现统一读取。 + +ThreadContext的使用方式有以下几种: + +- 解决方法一:提供ThreadContext包,在每次请求一开始时都复制系统里的所有配置缓存(复制过程要与配置更新Sync互斥),从而保证每次会话的数据的一致性。 +- 解决方法二:提供ThreadContext包,每次请求都绑定一个版本号,如果读取时版本号不一致则报错,需要重新请求。 +- 解决方法三:方法二的加强版,添加一个注解定义,标注它是需要强一制性的,每次会话读取时只复制这些强一制性配置(复制过程要与配置更新Sync互斥)。 +- 解决方法四:提供ThreadContext包,系统内保存有多个配置缓存层,读取时统一读取某个版本的缓存。每当配置更新时,缓存层增加。 + +第一种方法,代价太大。第二种方法,严重增加用户负担,第三种还是需要用户关心这个事情。我们将采用第四种方法。 + +**对于非Web项目:** + +比较难解决非一致性读取的问题。因为它没有了会话这样一个概念。Apache的FileChangedReloadingStrategy Reload配置文件的方案也没有解决此问题。所以,我们打算放弃这方面的解决。但是,我们还是会提供一个简单却Ugly的解决方案:提供函数来标识用户读取配置的边界。用户可以放弃使用这个方案,但是我们不保证不会发生“不一致读'问题。 + +## 配置放在哪里 ## + +关于配置应该放在哪里?有许多讨论,可以放在Web平台上,也可以放在Zookeeper上。从Disconf的实现中,可以看到Disconf是将配置数据放在Web平台的,而不是放在Zookeeper上的。那为什么这么设计呢?下面有个表格比较一下优劣。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
配置放在ZK上配置放在Disconf-web平台上比较
配置管理不易管理。ZK相比Mysql不易管理。用Mysql统一存储所有配置数据,非常方便管理,可扩展性强。配置放在Disconf-web显然容易管理和扩展。
职责分配ZK负责通知与配置数据存储,并提供给client获取数据。disconf-web负责新建、更新配置,并存储一些数据(非配置数据,用作管理),它不为client提供下载配置服务。ZK只负责通知。disconf-web负责新建、更新、存储 配置,并为client提供下载配置服务。如果采用第一种方案,数据分散存储了。第二种方案职责明确,数据统一存储在Disconf-web上,ZK只负责通知。
client的配置获取client启动时,需要从ZK上下载配置。因此必须使用disconf-web先写到ZK上,否则client启动时就无法使用最新配置。配置更新时,client直接从ZK获取最新配置。disconf-web必须统一的在ZK上新建、更新结点。client不管是启动还是更新时,均是从disconf-web上获取配置。启动时,client会在ZK监控结点(如果不存在,则新建);更新时,disconf-web更新ZK结点。disconf-web不会在ZK上新建结点。这两种方案其实都差不多
配置的一致性问题把数据存储在ZK上,无法像Mysql一样可持久化存储。ZK集群关闭后,数据全部丢失。client直接从ZK上获取配置数据,系统运行久之后,client的配置是否正确无从验证。数据存储在Mysql上,可持久化存储。不管未来是迁移或者扩展之类都非常方便。client是从Web上获取数据,然后再写到ZK上。理论上来说,ZK上的数据应该是与Web平台数据一样的,这可以作为验证平台正确性的一个方法。从可持久化性、可验证性方面来讲,第二个方案好。
配置获取配置存储在ZK上,非client想要获取配置,很不容易。配置存储在Web平台上,通过提供RestHttp接口,不管是谁想要获取配置都非常方便。第二种方案获取简单便捷。
+ + + diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 000000000..6f746162a --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,30 @@ +Disconf +======= + +Distributed Configuration Management Platform(分布式配置管理平台) + +专注于各种「分布式系统配置管理」的「通用组件」和「通用平台」, 提供统一的「配置管理服务」。 + +- disconf: https://github.com/knightliao/disconf +- demos: https://github.com/knightliao/disconf-demos-java +- wiki: https://github.com/knightliao/disconf/wiki + +推荐 + +- 有态度无广告的搜索引擎: https://www.sov5.com +- 高质量的微信公众号阅读: http://www.100weidu.com + +.. toctree:: + :maxdepth: 2 + :glob: + + install/index + quick/index + tutorial-client/index + tutorial-web/index + config/index + question/index + others/src/contribute + others/src/sponsor + others/src/update + design/index diff --git a/docs/source/install/01.md b/docs/source/install/01.md new file mode 100644 index 000000000..aedd680fa --- /dev/null +++ b/docs/source/install/01.md @@ -0,0 +1,9 @@ +#### disconf-client Install ### + +在您的 Maven POM 文件里加入: + + + com.baidu.disconf + disconf-client + 2.6.36 + \ No newline at end of file diff --git a/docs/source/install/02.md b/docs/source/install/02.md new file mode 100644 index 000000000..59763c1c7 --- /dev/null +++ b/docs/source/install/02.md @@ -0,0 +1,211 @@ +disconf-web安装 +=========== + +分布式配置Web平台服务 模块 + +推荐使用最新的Chrome或Firefox浏览. + +## 开放API + +- 让开发者具有自定义定制web控制台界面的能力: [Tutorial12 open api for web](../../tutorial-web/12-open-api-for-web.html) + +## 运行样式 + +### 主页 + +![](http://ww1.sinaimg.cn/mw1024/60c9620fgw1ekdfiw180rj20vt0gawfr.jpg) + +### 登录页 + +可以使用 admin admin 进行登录。 + +![](http://ww4.sinaimg.cn/mw1024/60c9620fgw1ekdfjkgbdcj20t70ie757.jpg) + +### 主界面 + +![](http://ww3.sinaimg.cn/mw1024/60c9620fgw1emxv1nw0u4j20qp0homy0.jpg) + +左上角可以选择APP和环境,选择之后,就会在中间出现若干个版本, + +选择版本后,就会显示 APP、环境、版本 三个条件下的配置列表: + +![](http://ww1.sinaimg.cn/mw1024/60c9620fgw1emyww39wjmj20qw0keq6m.jpg) + +#### 表格中 各个列的意义是: + +- APP:使用哪个APP,及它的ID +- KEY:配置文件或配置项 +- 配置内容:配置文件或配置项在配置中心中的值 +- 实例列表:使用此配置文件或配置项的所有实例列表,及每个实例的配置值。如果实例的配置值与配置中心的值不一致,这里会标识出来。 +- 修改时间:修改此配置的最后一次时间 +- 操作:个性、删除、下载 + +#### 右上角可以 + +新建配置项、新建配置文件、新建APP + +#### 表格右上方 + +可以批量下载所有配置文件至本地,还可以查看ZK上的部署情况。 + +## How to deploy ## + +### 安装依赖软件### + +- 安装Mysql(Ver 14.12 Distrib 5.0.45, for unknown-linux-gnu (x86_64) using EditLine wrapper) +- 安装Tomcat(apache-tomcat-7.0.50) +- 安装Nginx(nginx/1.5.3) +- 安装 zookeeeper (zookeeper-3.3.0) +- 安装 Redis (2.4.5) + +### 准备配置 ### + +**将你的配置文件放到此地址目录下(以下地址可自行设定):** + + /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: + + cp 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 + +### 上线前的初始化工作 ### + +**初始化数据库:** + +可以参考 sql/readme.md 来进行数据库的初始化。注意顺序执行 +0-init_table.sql +1-init_data.sql +201512/20151225.sql +20160701/20160701.sql + +里面默认有6个用户(**请注意线上环境删除这些用户以避免潜在的安全问题**) + +name | pwd +------- | ------- +admin | admin +testUser1 | MhxzKhl9209 +testUser2 | MhxzKhl167 +testUser3 | MhxzKhl783 +testUser4 | MhxzKhl8758 +testUser5 | MhxzKhl112 + +如果想自己设置初始化的用户名信息,可以参考代码来自己生成用户: + + src/main/java/com/baidu/disconf/web/tools/UserCreateTools.java + +### 部署War ### + +修改server.xml文件,在Host结点下设定Context: + + + +并设置端口为 8015 + +启动Tomcat,即可。 + +### 部署 前端 ### + +修改 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一样。 + +然后浏览器的访问域名也是这个。 + +## 业务功能 ## + +- 支持用户登录/登出 +- 浏览配置 + - 按 APP/版本/环境 选择 +- 修改配置 + - 修改配置项 + - 修改配置文件 +- 新建配置 + - 新建配置项 + - 新建配置文件 + - 新建APP + +## 架构方案 ## + +Nginx(处理静态请求) + Tomcat(处理动态请求) + +- **后端** + - SpringMvc(Spring 4.1.7.RELEASE) + - Jdbc-Template + - Mysql + - RestFul API + - Redis for user login/logout + - H2内存数据库测试方案/Junit/SpringTest +- **前端** + - HTML + - Jquery(1.10.4):JS工具集合 + - Bootstrap(2.3.2):界面UI + - Node(ejs/fs/eventproxy): 用于前端的HTML的模板化管理 +- **前后端接口(前后端分离)** + - 完全Ajax接口 + - JSON + - RestFul API + + diff --git a/docs/source/install/index.rst b/docs/source/install/index.rst new file mode 100644 index 000000000..10c3788dc --- /dev/null +++ b/docs/source/install/index.rst @@ -0,0 +1,12 @@ +Install +======= + +目前项目包含了 客户端disconf-Client和 管理端disconf-Web两个模块。目前所有模块代码均是Java实现。 + + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/01 + src/02 \ No newline at end of file diff --git a/docs/source/install/src/01.rst b/docs/source/install/src/01.rst new file mode 100644 index 000000000..c6331d4a4 --- /dev/null +++ b/docs/source/install/src/01.rst @@ -0,0 +1,12 @@ +disconf-client Install +^^^^^^^^^^^^^^^^^^^^^^ + +在您的 Maven POM 文件里加入: + +:: + + + com.baidu.disconf + disconf-client + 2.6.36 + diff --git a/docs/source/install/src/02.rst b/docs/source/install/src/02.rst new file mode 100644 index 000000000..469252be2 --- /dev/null +++ b/docs/source/install/src/02.rst @@ -0,0 +1,270 @@ +disconf-web安装 +=============== + +分布式配置Web平台服务 模块 + +推荐使用最新的Chrome或Firefox浏览. + +开放API +------- + +- 让开发者具有自定义定制web控制台界面的能力: `Tutorial12 open api for + web <../../tutorial-web/12-open-api-for-web.html>`__ + +运行样式 +-------- + +主页 +~~~~ + +|image0| + +登录页 +~~~~~~ + +可以使用 admin admin 进行登录。 + +|image1| + +主界面 +~~~~~~ + +|image2| + +左上角可以选择APP和环境,选择之后,就会在中间出现若干个版本, + +选择版本后,就会显示 APP、环境、版本 三个条件下的配置列表: + +|image3| + +表格中 各个列的意义是: +^^^^^^^^^^^^^^^^^^^^^^^ + +- APP:使用哪个APP,及它的ID +- KEY:配置文件或配置项 +- 配置内容:配置文件或配置项在配置中心中的值 +- 实例列表:使用此配置文件或配置项的所有实例列表,及每个实例的配置值。如果实例的配置值与配置中心的值不一致,这里会标识出来。 +- 修改时间:修改此配置的最后一次时间 +- 操作:个性、删除、下载 + +右上角可以 +^^^^^^^^^^ + +新建配置项、新建配置文件、新建APP + +表格右上方 +^^^^^^^^^^ + +可以批量下载所有配置文件至本地,还可以查看ZK上的部署情况。 + +How to deploy +------------- + +安装依赖软件 +~~~~~~~~~~~~ + +- 安装Mysql(Ver 14.12 Distrib 5.0.45, for unknown-linux-gnu (x86\_64) + using EditLine wrapper) +- 安装Tomcat(apache-tomcat-7.0.50) +- 安装Nginx(nginx/1.5.3) +- 安装 zookeeeper (zookeeper-3.3.0) +- 安装 Redis (2.4.5) + +准备配置 +~~~~~~~~ + +**将你的配置文件放到此地址目录下(以下地址可自行设定):** + +:: + + /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: + +:: + + cp 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 + +上线前的初始化工作 +~~~~~~~~~~~~~~~~~~ + +**初始化数据库:** + +| 可以参考 sql/readme.md 来进行数据库的初始化。注意顺序执行 +| 0-init\_table.sql +| 1-init\_data.sql +| 201512/20151225.sql +| 20160701/20160701.sql + +里面默认有6个用户(\ **请注意线上环境删除这些用户以避免潜在的安全问题**\ ) + ++-------------+---------------+ +| name | pwd | ++=============+===============+ +| admin | admin | ++-------------+---------------+ +| testUser1 | MhxzKhl9209 | ++-------------+---------------+ +| testUser2 | MhxzKhl167 | ++-------------+---------------+ +| testUser3 | MhxzKhl783 | ++-------------+---------------+ +| testUser4 | MhxzKhl8758 | ++-------------+---------------+ +| testUser5 | MhxzKhl112 | ++-------------+---------------+ + +如果想自己设置初始化的用户名信息,可以参考代码来自己生成用户: + +:: + + src/main/java/com/baidu/disconf/web/tools/UserCreateTools.java + +部署War +~~~~~~~ + +修改server.xml文件,在Host结点下设定Context: + +:: + + + +并设置端口为 8015 + +启动Tomcat,即可。 + +部署 前端 +~~~~~~~~~ + +修改 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一样。 + +然后浏览器的访问域名也是这个。 + +业务功能 +-------- + +- 支持用户登录/登出 +- 浏览配置 + + - 按 APP/版本/环境 选择 + +- 修改配置 + + - 修改配置项 + - 修改配置文件 + +- 新建配置 + + - 新建配置项 + - 新建配置文件 + - 新建APP + +架构方案 +-------- + +Nginx(处理静态请求) + Tomcat(处理动态请求) + +- **后端** + + - SpringMvc(Spring 4.1.7.RELEASE) + - Jdbc-Template + - Mysql + - RestFul API + - Redis for user login/logout + - H2内存数据库测试方案/Junit/SpringTest + +- **前端** + + - HTML + - Jquery(1.10.4):JS工具集合 + - Bootstrap(2.3.2):界面UI + - Node(ejs/fs/eventproxy): 用于前端的HTML的模板化管理 + +- **前后端接口(前后端分离)** + + - 完全Ajax接口 + - JSON + - RestFul API + +.. |image0| image:: http://ww1.sinaimg.cn/mw1024/60c9620fgw1ekdfiw180rj20vt0gawfr.jpg +.. |image1| image:: http://ww4.sinaimg.cn/mw1024/60c9620fgw1ekdfjkgbdcj20t70ie757.jpg +.. |image2| image:: http://ww3.sinaimg.cn/mw1024/60c9620fgw1emxv1nw0u4j20qp0homy0.jpg +.. |image3| image:: http://ww1.sinaimg.cn/mw1024/60c9620fgw1emyww39wjmj20qw0keq6m.jpg + diff --git a/docs/source/others/contribute.md b/docs/source/others/contribute.md new file mode 100644 index 000000000..0210134fc --- /dev/null +++ b/docs/source/others/contribute.md @@ -0,0 +1,68 @@ +社区贡献 +======= + +## 其他disconf-web开源实现 + +- https://github.com/comlkz/disconf-web + +## 其他disconf-client开源实现 + +- Node: https://github.com/Corey600/node-disconf-client + +## 网友贡献/使用教程 + +- [cnblog: 马进举 统一配置管理-百度disconf ](http://www.cnblogs.com/majinju/p/4502246.html) +- [csdn: 分布式配置管理平台 - Disconf web管理端安装](http://blog.csdn.net/zhu_tianwei/article/details/49512751) +- [disconf的尝试](http://mysrc.sinaapp.com/view_note/?id=573) +- [开源分布式配置中心选型](http://vernonzheng.com/2015/02/09/%E5%BC%80%E6%BA%90%E5%88%86%E5%B8%83%E5%BC%8F%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E9%80%89%E5%9E%8B/) +- [disconf基于XML实现](http://blog.csdn.net/hyg1683116633/article/details/47339187) +- [Docker-Disconf](http://git.oschina.net/gongxusheng/docker-disconf) + +## 媒体报道 + +- [开源中国社区](http://www.oschina.net/p/disconf) +- [开发者头条:Disconf - 一个 Java 实现的分布式配置管理平台](http://toutiao.io/posts/ljg97) +- [微头条:百度 disconf 分布式配置管理平台](http://www.wtoutiao.com/p/d1dvfL.html) +- [UDN: [运维相关] 分布式配置管理平台 Disconf](http://udn.yyuap.com/thread-32595-1-1.html) +- [51testing: 百度disconf分布式配置管理平台](http://www.51testing.com/html/74/n-2459674-3.html) +- [推酷 Disconf —— 来自百度的分布式配置管理平台](http://www.tuicool.com/articles/QniqQn7) +- [实施微服务需要哪些基础框架|技术热点 ](http://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=400645575&idx=1&sn=da55d75db55117046c520de88dde1123&3rd=MzA3MDU4NTYzMw==&scene=6#rd) + +## 正在使用公司列表 + +- [百度](20+条产品线使用) +- [滴滴出行(上海/北京)](http://www.xiaojukeji.com/) +- [银联] +- [网易](http://www.163.com/) +- [苏宁易购](http://www.suning.com) (搜索中心数据处理平台) +- 顺丰科技 +- [润生活](http://www.szzjcs.com/) (千万融资,全线产品使用) +- [拉勾网](http://www.lagou.com/) +- [人脉通](http://renmaitong.com/) (目前已B轮融资,4条产品线使用) +- [普联(Tp-link)技术有限公司](http://www.tp-link.com.cn/) +- [杭州数梦工场科技有限公司](http://www.dtdream.com) +- [众钱网](http://17money.com) +- [快速递](http://www.ksudi.com) +- [杭州同盾科技](https://www.tongdun.cn/) +- [杭州趣维科技](http://www.xiaoying.tv/) (数千万RMB A轮投资) +- [百世物流科技](http://www.800best.com/) (在全国建立了400多个运作中心和250万平米的仓库及转运中心,拥有30000多员工和上万个认证加盟商及合作伙伴) +- [仙人掌股票](http://www.icaikee.com/)(2015年度最火app,所有产品线已全面接入disconf) +- [多点APP](http://www.dmall.com/) +- [上海华禽网络科技有限公司](www.huaqinwang.com) (整个公司所有产品线均在使用) +- 新东方在线 +- 深圳斯凯荣科技 +- [更多](https://github.com/knightliao/disconf/issues/18) + +## 正在关注的公司列表 + +- 洋码头 +- 新意互动 + + + + + + + + + diff --git a/docs/source/others/index.rst b/docs/source/others/index.rst new file mode 100644 index 000000000..ab738a910 --- /dev/null +++ b/docs/source/others/index.rst @@ -0,0 +1,7 @@ +其它资源 +============ + + +.. toctree:: + :maxdepth: 2 + :numbered: 2 diff --git a/docs/source/others/sponsor.md b/docs/source/others/sponsor.md new file mode 100644 index 000000000..744ba62b2 --- /dev/null +++ b/docs/source/others/sponsor.md @@ -0,0 +1,14 @@ +联系和赞助 +======== + +## 联系我 + +- weibo: [http://weibo.com/knightliao](http://weibo.com/knightliao) +- wechat: knightliao +- 主页: http://liaoqiqi.com + +## 赞助 + +如果您觉得disconf不错,可以资助作者, 资助公司或个人会留下名字。 + +![](http://ww3.sinaimg.cn/bmiddle/60c9620fjw1ergyopdbxpj207i07iaag.jpg) \ No newline at end of file diff --git a/docs/source/others/src/contribute.rst b/docs/source/others/src/contribute.rst new file mode 100644 index 000000000..5033dcd14 --- /dev/null +++ b/docs/source/others/src/contribute.rst @@ -0,0 +1,74 @@ +社区贡献 +======== + +其他disconf-web开源实现 +----------------------- + +- https://github.com/comlkz/disconf-web + +其他disconf-client开源实现 +-------------------------- + +- Node: https://github.com/Corey600/node-disconf-client + +网友贡献/使用教程 +----------------- + +- `cnblog: 马进举 + 统一配置管理-百度disconf `__ +- `csdn: 分布式配置管理平台 - Disconf + web管理端安装 `__ +- `disconf的尝试 `__ +- `开源分布式配置中心选型 `__ +- `disconf基于XML实现 `__ +- `Docker-Disconf `__ + +媒体报道 +-------- + +- `开源中国社区 `__ +- `开发者头条:Disconf - 一个 Java + 实现的分布式配置管理平台 `__ +- `微头条:百度 disconf + 分布式配置管理平台 `__ +- `UDN: [运维相关] 分布式配置管理平台 + Disconf `__ +- `51testing: + 百度disconf分布式配置管理平台 `__ +- `推酷 Disconf —— + 来自百度的分布式配置管理平台 `__ +- `实施微服务需要哪些基础框架|技术热点 `__ + +正在使用公司列表 +---------------- + +- [百度](20+条产品线使用) +- `滴滴出行(上海/北京) `__ +- [银联] +- `网易 `__ +- `苏宁易购 `__ (搜索中心数据处理平台) +- 顺丰科技 +- `润生活 `__ (千万融资,全线产品使用) +- `拉勾网 `__ +- `人脉通 `__ (目前已B轮融资,4条产品线使用) +- `普联(Tp-link)技术有限公司 `__ +- `杭州数梦工场科技有限公司 `__ +- `众钱网 `__ +- `快速递 `__ +- `杭州同盾科技 `__ +- `杭州趣维科技 `__ (数千万RMB A轮投资) +- `百世物流科技 `__ + (在全国建立了400多个运作中心和250万平米的仓库及转运中心,拥有30000多员工和上万个认证加盟商及合作伙伴) +- `仙人掌股票 `__\ (2015年度最火app,所有产品线已全面接入disconf) +- `多点APP `__ +- `上海华禽网络科技有限公司 `__ + (整个公司所有产品线均在使用) +- 新东方在线 +- 深圳斯凯荣科技 +- `更多 `__ + +正在关注的公司列表 +------------------ + +- 洋码头 +- 新意互动 diff --git a/docs/source/others/src/sponsor.rst b/docs/source/others/src/sponsor.rst new file mode 100644 index 000000000..47e48991f --- /dev/null +++ b/docs/source/others/src/sponsor.rst @@ -0,0 +1,19 @@ +联系和赞助 +========== + +联系我 +------ + +- weibo: http://weibo.com/knightliao +- wechat: knightliao +- 主页: http://liaoqiqi.com + +赞助 +---- + +如果您觉得disconf不错,可以资助作者, 资助公司或个人会留下名字。 + +|image0| + +.. |image0| image:: http://ww3.sinaimg.cn/bmiddle/60c9620fjw1ergyopdbxpj207i07iaag.jpg + diff --git a/docs/source/others/src/update.rst b/docs/source/others/src/update.rst new file mode 100644 index 000000000..8f5a53c90 --- /dev/null +++ b/docs/source/others/src/update.rst @@ -0,0 +1,438 @@ +版本更新 +-------- + +2.6.6 发布于 20160911 + +- disconf-web: + + - 配置和配置项可支持自定义app + https://github.com/knightliao/disconf/issues/147 + +- disconf-client: + + - 支持https的web端 https://github.com/knightliao/disconf/issues/158 + - path支持windows: https://github.com/knightliao/disconf/issues/166 + - 删除类 DisconfMgrJustHostFileBean + - DisconfFile 的属性 copy2TargetDirPath 更改为 targetDirPath + +2.6.35 +------ + +2016年7月1号 + +- disconf-client: + + - fix bug: support bean annotation @Value (但是不支持reload特性) + - fix bug: + 当同时使用XML式和注解式时,当修改配置时,XML式的BEAN也可以重新被注入 + https://github.com/knightliao/disconf/issues/70 + +- disconf-web: + + - 支持自定义数据库名 + + - 需要修改 jdbc-mysql.properties : + + - 以前是:jdbc.db\_0.url=jdbc:mysql://127.0.0.1:3306?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=false + - 现在是 + jdbc.db\_0.url=jdbc:mysql://127.0.0.1:3306/disconf?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull + &rewriteBatchedStatements=false + + - 提供修改密码功能 + + - 需要执行 disconf-web/sql/20160701/20160701.sql + + - 提供生成密码的工具 + + - /disconf-web/bin/sql/makeSql.py + + - 支持client两个api + + - /api/config/list 可以自由的 指定app env version + 的配置项列表(包含值) + - /api/config/simple/list 可以自由的 指定app env version + 的配置项列表(不包含值) + +2.6.34 +------ + +2016年5月31号 + +- disconf-client: + + - 删除临时创建的lock文件 + + - https://github.com/knightliao/disconf/issues/79 + + - 支持从自定义路径读取 disconf.properties + + - 例如使用 -Ddisconf.conf=/tmp/disconf.properties 来读取 + disconf.properties 文件。默认是从classpath根目录读取此文件。 + + - issue: https://github.com/knightliao/disconf/issues/93 + - 说明: + hhttp://disconf.readthedocs.io/zh\_CN/stable/config/src/client-config.html#disconf-client + + - 增加统一的类 来个性化编程式的获取任何配置数据 + + - issue: https://github.com/knightliao/disconf/issues/94 + - 说明: + http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/config-getter.html + + - fix bug: + + - 使用xml分布式配置时,当disconf.ignore掉某个配置文件时,启动后会抛异常说该配置文件找不到。修复后不会再报错 + - 新增配置项,值可以为空格,修改该配置项,就算改为有效字符,都会提示“服务器内部错误” + + - https://github.com/knightliao/disconf/issues/77 + + - java.lang.IllegalArgumentException: wrong number of arguments + + - https://github.com/knightliao/disconf/issues/89 + + - upgrade common-lang -> common-lang3 + + - https://github.com/knightliao/disconf/issues/81 + +- disconf-web: + + - 删除web的jackson依赖 + + - https://github.com/knightliao/disconf/issues/82 + + - upgrade common-lang -> common-lang3 + + - https://github.com/knightliao/disconf/issues/81 + +2.6.33 +------ + +2016月5月07日 + +- disconf-client: + + - (随着以-jar方式启动的程序变得越来越流行)当使用以-jar方式启动的程序(非tomcat,web方式)时,例如springboot时,可以无缝对接(不会出现配置文件找不到的情况)。 + + - 说明: + http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/jar-start-up.html + +2.6.32 +------ + +2016月3月27日 + +- disconf-client: + + - 增加统一的回调类,unify-notify模式:灵活处理更新配置通知(方便大家在这里自由控制更新逻辑)\ `issue-67 `__ + `Tutorial-13 `__ + - 配置初始化或更新时,通知采用 "bean setter模式": + 在注入配置到实例时,优先使用set方法(方便大家在这里写自己逻辑代码),其次才是反射注入。 + `Tutorial-14 `__ + +2.6.31 +------ + +发布于:2016月1月8日 + +- disconf-client: + + - 精减库依赖,去掉使用jersey库,避免库冲突: 改采用简单的httpclient下载 + +- disconf-web: + + - 支持历史操作记录第一期,支持数据库记录。 + + - 需要更新SQL: + https://github.com/knightliao/disconf/blob/master/disconf-web/sql/201512/20151225.sql + +2.6.30 +------ + +发布于:2015年12月1日 + +- disconf-client: + + - 不再需要将\ ``com.baidu``\ 加入扫描包了,只需要扫描自己的包即可。 + + 原来的方式 + + :: + + + + 现在的 + + :: + + + +2.6.29 +------ + +存在BUG,已废弃 + +2.6.28 +------ + +发布于:2015年11月20日 + +- disconf-client: + + - 每个配置文件的路径支持个性化指定,不一定非是classpath + + - http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial11-config-download-path.html + + - 支持spring-boot jar包识别方式 + 参见:\ https://github.com/knightliao/disconf-demos-java/tree/master/disconf-spring-boot-demo + - 修复bug: + + - 在高于spring-bean3.1.2版本中出现基于XML配置的配置文件无法reload + + - 使 disconf.enable\_local\_download\_dir\_in\_class\_path + 该配置用户可以自行配置: + + - http://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html + +- 精减依赖项 + +2.6.27 +------ + +发布于:2015年10月26日 + +- disconf-client: + + - fix bean order 问题 + +- disconf-web: + + - fix 上传文件(使用贴文本方式)的version无法指定的bug + +2.6.26 +------ + +发布于:2015年10月26日 + +- disconf-client: + + - 实现真正意义上的统一上线包:disconf-client 配置文件 + disconf.properties 的 所有配置项均支持环境变量方式(命令行)传入 + 均支持 + + - 说明:\ `配置说明 `__ + - `Tutorial 9 + 实现真正意义上的统一上线包 `__ + + - (重要)更新disconf.properties中所有配置项,均加上前缀 + "disconf."(此升级具有兼容性,原有配置亦可以运行,但推荐升级) + + - 说明: + `配置说明 `__ + +- disconf-web: + + - 新建配置时 app下拉页面被截断bug修复 + https://github.com/knightliao/disconf/issues/22 + +2.6.25 +------ + +发布于:2015年8月20日 + +- disconf-client: + + - 实现真正意义上的统一上线包:disconf-client 配置文件 + disconf.properties 的 app,env,version 均支持 + 环境变量方式(命令行)参数传入方式 + + - 说明: + `配置说明 `__ + - `Tutorial 9 + 实现真正意义上的统一上线包 `__ + + - disconf.properties支持 user\_define\_download\_dir + 项目,用户可以指定将配置下载到你想要的目录 + + - 说明: + `配置说明 `__ + - `Tutorial 10 + 实现一个配置更新下载器agent `__ + + - fix bugs + +- disconf-demos + + - `disconf-spring-boot-demo `__: + 使用disconf的spring-boot demo程序,更少的配置 + +2.6.24 +------ + +发布于:2015年7月3日 + +- disconf-client: + + - fix bug https://github.com/knightliao/disconf/issues/11 + +2.6.23 +------ + +发布于:2015年7月2日 + +- disconf-client: + + - 增加功能:scanPackage 增加扫描多包功能,逗号分隔,例如: + + :: + + + + + +2.6.22 +------ + +发布于:2015年6月3日 + +- disconf-client: + + - fix bug: + 当enable.remote.conf为false时,disconf-client可能无法读取本地配置的问题 + +2.6.21 +------ + +发布于:2015年4月14日 + +- disconf-client: + + - 其它小修改 + - 优化 pom.xml + +2.6.20 +------ + +发布于:2015年3月27日 + +- disconf-client: + + - `支持基于XML配置的,无任何代码侵入的 + 分布式配置 `__ + +2.6.19 +------ + +发布于:2015年1月22日 + +- disconf-client: + + - `支持任意文件的配置托管 `__ + - `回调时支持以配置key作为key `__ + +- disconf-web: + + - 支持角色系统【普通,管理员,只读管理员】 + - 当配置文件里面含有unicode时,支持显示成UTF8 + +2.6.18 +------ + +发布于:2014年12月19日 + +- disconf-client: + + - FIX BUG: + 同一台机器多个实例使用同一个classpath下的并发设置配置文件BUG(非常重要) + - FIX BUG: disconf store use 'get' (非常重要) + +2.6.16 +------ + +发布于:2014年12月3日 + +- disconf-client: + + - fix zookeeper session expired error: + 当ZK集群不可用时,disconf-client可以自动重连,并保证配置watch信息不丢失。 + +- disconf-web: + + - `主页配置获取、ZK监控情况改成ajax请求(为了避免主页载入数据太多) `__ + - `支持新建、修改任何配置时发送邮件通知 `__ + - `支持多用户对多APP的权限控制 `__ + - `支持定时校验中心的配置 和 + 多客户端配置的一致性 `__ + +2.6.15 +------ + +发布于:2014年11月7日 + +- disconf-client: + + - `非注解式(托管式)的配置文件添加,增加额外的定义方式。原有的方式(2.6.14版本)的方式亦兼容,但不推荐使用。 `__ + - `非注解式(托管式)的配置文件额外支持xml(以前仅支持properties)格式。 `__ + +2.6.14 +------ + +发布于:2014年9月18日 + +- disconf-client: + + - `支持非注解方式(托管式)的配置文件统一化(只支持.propertes格式) `__ + - fix bug: 静态配置文件无法动态更新的BUG + - ZK session expire time enlarge from 5 to 30 seconds + - `支持自定义过滤分布式配置 `__ + +- disconf-web: 支持更便捷人性化的ZK查询 + + - 配置文件的输入支持 直接文本输入+上传配置文件方式 + - 支持配置文件下载,批量下载 + - 支持显示配置所影响的机器源,并提供配置数据校验工具 + - 全新Web主页 + +2.6.13 +------ + +发布于:2014年9月4日 + +- fix bug: 配置里解析Integer(或类似非String)数据时出错 +- Zoo Preifix: client get this value from server, not from local config +- fix bug: + disconf不是最高优先级启动,导致在本地没有配置文件时,PropertyPlaceholderConfigurer在Disconf启动前初始化, + location为空,因此它认为没有配置文件存在,出现Spring启动失败。 + 修改方法是,使用BeanDefinitionRegistryPostProcessor使Disconf最高优先级启动,这样后面执行PropertyPlaceholderConfigurer初始化 + 时就可以发现所有的配置文件。 + +2.6.11 & 2.6.12 +--------------- + +- 修复BUG: 当不使用Disconf时,close会有Null异常 +- 打日志策略更新:原则上日志为Debug,出错为ERROR,需要注意为WARN + +2.6.10 +------ + +- change log: + + - 注入静态配置域时不再打印错误字段 + - 配置完成后打印配置仓库时打印方式pretty化 + +- 修复BUG: + 支持空配置文件类,如EmptyConf.java,可以使用它来实现简单的同步 + +2.6.9 +----- + +- FixBug:找不到 disconf\_sys.properties +- 增加功能: + + - 支持静态配置文件分布式 + - 支持配置配置项分布式 + +2.6.8 +----- + +- Init Version diff --git a/docs/source/others/src/users.rst b/docs/source/others/src/users.rst new file mode 100644 index 000000000..7c12734dc --- /dev/null +++ b/docs/source/others/src/users.rst @@ -0,0 +1,36 @@ +用户列表 +======== + +使用公司列表 +------------ + +- [百度](20+条产品线使用) +- `滴滴出行(上海/北京) `__ +- [银联] +- `网易 `__ +- `苏宁易购 `__ (搜索中心数据处理平台) +- 顺丰科技 +- `润生活 `__ (千万融资,全线产品使用) +- `拉勾网 `__ +- `人脉通 `__ (目前已B轮融资,4条产品线使用) +- `普联(Tp-link)技术有限公司 `__ +- `杭州数梦工场科技有限公司 `__ +- `众钱网 `__ +- `快速递 `__ +- `杭州同盾科技 `__ +- `杭州趣维科技 `__ (数千万RMB A轮投资) +- `百世物流科技 `__ + (在全国建立了400多个运作中心和250万平米的仓库及转运中心,拥有30000多员工和上万个认证加盟商及合作伙伴) +- `仙人掌股票 `__\ (2015年度最火app,所有产品线已全面接入disconf) +- `多点APP `__ +- `上海华禽网络科技有限公司 `__ + (整个公司所有产品线均在使用) +- 新东方在线 +- 深圳斯凯荣科技 +- `更多 `__ + +正在关注 +-------- + +- 洋码头 +- 新意互动 diff --git "a/docs/source/others/src/\345\252\222\344\275\223\346\212\245\351\201\223\344\270\216\347\275\221\345\217\213\346\225\231\347\250\213.rst" "b/docs/source/others/src/\345\252\222\344\275\223\346\212\245\351\201\223\344\270\216\347\275\221\345\217\213\346\225\231\347\250\213.rst" new file mode 100644 index 000000000..19f76aa03 --- /dev/null +++ "b/docs/source/others/src/\345\252\222\344\275\223\346\212\245\351\201\223\344\270\216\347\275\221\345\217\213\346\225\231\347\250\213.rst" @@ -0,0 +1,30 @@ +网友贡献&&媒体报道 +================== + +网友贡献/使用教程 +----------------- + +- `cnblog: 马进举 + 统一配置管理-百度disconf `__ +- `csdn: 分布式配置管理平台 - Disconf + web管理端安装 `__ +- `disconf的尝试 `__ +- `开源分布式配置中心选型 `__ +- `disconf基于XML实现 `__ +- `Docker-Disconf `__ + +媒体报道 +-------- + +- `开源中国社区 `__ +- `开发者头条:Disconf - 一个 Java + 实现的分布式配置管理平台 `__ +- `微头条:百度 disconf + 分布式配置管理平台 `__ +- `UDN: [运维相关] 分布式配置管理平台 + Disconf `__ +- `51testing: + 百度disconf分布式配置管理平台 `__ +- `推酷 Disconf —— + 来自百度的分布式配置管理平台 `__ +- `实施微服务需要哪些基础框架|技术热点 `__ diff --git a/docs/source/others/update.md b/docs/source/others/update.md new file mode 100644 index 000000000..ad70113f0 --- /dev/null +++ b/docs/source/others/update.md @@ -0,0 +1,290 @@ + +## 版本更新 + +2.6.6 发布于 20160911 + +- disconf-web: + - 配置和配置项可支持自定义app https://github.com/knightliao/disconf/issues/147 +- disconf-client: + - 支持https的web端 https://github.com/knightliao/disconf/issues/158 + - path支持windows: https://github.com/knightliao/disconf/issues/166 + - 删除类 DisconfMgrJustHostFileBean + - DisconfFile 的属性 copy2TargetDirPath 更改为 targetDirPath + + +## 2.6.35 ## + +2016年7月1号 + +- disconf-client: + - fix bug: support bean annotation @Value (但是不支持reload特性) + - fix bug: 当同时使用XML式和注解式时,当修改配置时,XML式的BEAN也可以重新被注入 https://github.com/knightliao/disconf/issues/70 +- disconf-web: + - 支持自定义数据库名 + - 需要修改 jdbc-mysql.properties : + - 以前是:jdbc.db_0.url=jdbc:mysql://127.0.0.1:3306?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=false + - 现在是 jdbc.db_0.url=jdbc:mysql://127.0.0.1:3306/disconf?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull + &rewriteBatchedStatements=false + - 提供修改密码功能 + - 需要执行 disconf-web/sql/20160701/20160701.sql + - 提供生成密码的工具 + - /disconf-web/bin/sql/makeSql.py + - 支持client两个api + - /api/config/list 可以自由的 指定app env version 的配置项列表(包含值) + - /api/config/simple/list 可以自由的 指定app env version 的配置项列表(不包含值) + +## 2.6.34 ## + +2016年5月31号 + +- disconf-client: + - 删除临时创建的lock文件 + - https://github.com/knightliao/disconf/issues/79 + - 支持从自定义路径读取 disconf.properties + - 例如使用 -Ddisconf.conf=/tmp/disconf.properties 来读取 disconf.properties 文件。默认是从classpath根目录读取此文件。 + - issue: https://github.com/knightliao/disconf/issues/93 + - 说明: hhttp://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html#disconf-client + - 增加统一的类 来个性化编程式的获取任何配置数据 + - issue: https://github.com/knightliao/disconf/issues/94 + - 说明: http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/config-getter.html + - fix bug: + - 使用xml分布式配置时,当disconf.ignore掉某个配置文件时,启动后会抛异常说该配置文件找不到。修复后不会再报错 + - 新增配置项,值可以为空格,修改该配置项,就算改为有效字符,都会提示“服务器内部错误” + - https://github.com/knightliao/disconf/issues/77 + - java.lang.IllegalArgumentException: wrong number of arguments + - https://github.com/knightliao/disconf/issues/89 + - upgrade common-lang -> common-lang3 + - https://github.com/knightliao/disconf/issues/81 +- disconf-web: + - 删除web的jackson依赖 + - https://github.com/knightliao/disconf/issues/82 + - upgrade common-lang -> common-lang3 + - https://github.com/knightliao/disconf/issues/81 + +## 2.6.33 ## + +2016月5月07日 + +- disconf-client: + - (随着以-jar方式启动的程序变得越来越流行)当使用以-jar方式启动的程序(非tomcat,web方式)时,例如springboot时,可以无缝对接(不会出现配置文件找不到的情况)。 + - 说明: http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/jar-start-up.html + +## 2.6.32 ## + +2016月3月27日 + +- disconf-client: + - 增加统一的回调类,unify-notify模式:灵活处理更新配置通知(方便大家在这里自由控制更新逻辑)[issue-67](https://github.com/knightliao/disconf/issues/67) + [Tutorial-13](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial13-unify-notify.html) + - 配置初始化或更新时,通知采用 "bean setter模式": 在注入配置到实例时,优先使用set方法(方便大家在这里写自己逻辑代码),其次才是反射注入。 + [Tutorial-14](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial14-bean-setter-mode.html) + +## 2.6.31 ## + +发布于:2016月1月8日 + +- disconf-client: + - 精减库依赖,去掉使用jersey库,避免库冲突: 改采用简单的httpclient下载 +- disconf-web: + - 支持历史操作记录第一期,支持数据库记录。 + - 需要更新SQL: https://github.com/knightliao/disconf/blob/master/disconf-web/sql/201512/20151225.sql + +## 2.6.30 ## + +发布于:2015年12月1日 + +- disconf-client: + - 不再需要将`com.baidu`加入扫描包了,只需要扫描自己的包即可。 + + 原来的方式 + + + + 现在的 + + + +## 2.6.29 ## + +存在BUG,已废弃 + +## 2.6.28 ## + +发布于:2015年11月20日 + +- disconf-client: + - 每个配置文件的路径支持个性化指定,不一定非是classpath + - http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial11-config-download-path.html + - 支持spring-boot jar包识别方式 参见:https://github.com/knightliao/disconf-demos-java/tree/master/disconf-spring-boot-demo + - 修复bug: + - 在高于spring-bean3.1.2版本中出现基于XML配置的配置文件无法reload + - 使 disconf.enable_local_download_dir_in_class_path 该配置用户可以自行配置: + - http://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html +- 精减依赖项 + +## 2.6.27 ## + +发布于:2015年10月26日 + +- disconf-client: + - fix bean order 问题 +- disconf-web: + - fix 上传文件(使用贴文本方式)的version无法指定的bug + +## 2.6.26 ## + +发布于:2015年10月26日 + +- disconf-client: + - 实现真正意义上的统一上线包:disconf-client 配置文件 disconf.properties 的 所有配置项均支持环境变量方式(命令行)传入 均支持 + - 说明:[配置说明](http://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html) + - [Tutorial 9 实现真正意义上的统一上线包](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial9.html) + - (重要)更新disconf.properties中所有配置项,均加上前缀 "disconf."(此升级具有兼容性,原有配置亦可以运行,但推荐升级) + - 说明: [配置说明](http://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html) +- disconf-web: + - 新建配置时 app下拉页面被截断bug修复 https://github.com/knightliao/disconf/issues/22 + +## 2.6.25 ## + +发布于:2015年8月20日 + +- disconf-client: + - 实现真正意义上的统一上线包:disconf-client 配置文件 disconf.properties 的 app,env,version 均支持 环境变量方式(命令行)参数传入方式 + - 说明: [配置说明](http://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html) + - [Tutorial 9 实现真正意义上的统一上线包](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial9.html) + - disconf.properties支持 user_define_download_dir 项目,用户可以指定将配置下载到你想要的目录 + - 说明: [配置说明](http://disconf.readthedocs.io/zh_CN/stable/config/src/client-config.html) + - [Tutorial 10 实现一个配置更新下载器agent](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial10.html) + - fix bugs +- disconf-demos + - [disconf-spring-boot-demo](https://github + .com/knightliao/disconf/tree/dev/disconf-demos/disconf-spring-boot-demo): 使用disconf的spring-boot demo程序,更少的配置 + +## 2.6.24 ## + +发布于:2015年7月3日 + +- disconf-client: + - fix bug https://github.com/knightliao/disconf/issues/11 + +## 2.6.23 ## + +发布于:2015年7月2日 + +- disconf-client: + - 增加功能:scanPackage 增加扫描多包功能,逗号分隔,例如: + + + + + +## 2.6.22 ## + +发布于:2015年6月3日 + +- disconf-client: + - fix bug: 当enable.remote.conf为false时,disconf-client可能无法读取本地配置的问题 + +## 2.6.21 ## + +发布于:2015年4月14日 + +- disconf-client: + - 其它小修改 + - 优化 pom.xml + +## 2.6.20 ## + +发布于:2015年3月27日 + +- disconf-client: + - [支持基于XML配置的,无任何代码侵入的 分布式配置](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial8.html) + +## 2.6.19 ## + +发布于:2015年1月22日 + +- disconf-client: + - [支持任意文件的配置托管](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial5.html) + - [回调时支持以配置key作为key](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial2.html) +- disconf-web: + - 支持角色系统【普通,管理员,只读管理员】 + - 当配置文件里面含有unicode时,支持显示成UTF8 + +## 2.6.18 ## + +发布于:2014年12月19日 + +- disconf-client: + - FIX BUG: 同一台机器多个实例使用同一个classpath下的并发设置配置文件BUG(非常重要) + - FIX BUG: disconf store use 'get' (非常重要) + +## 2.6.16 ## + +发布于:2014年12月3日 + +- disconf-client: + - fix zookeeper session expired error: 当ZK集群不可用时,disconf-client可以自动重连,并保证配置watch信息不丢失。 +- disconf-web: + - [主页配置获取、ZK监控情况改成ajax请求(为了避免主页载入数据太多)](http://disconf.readthedocs.io/zh_CN/stable/tutorial-web/src/Tutorial6.html) + - [支持新建、修改任何配置时发送邮件通知](hhttp://disconf.readthedocs.io/zh_CN/stable/tutorial-web/src/Tutorial6.html) + - [支持多用户对多APP的权限控制](http://disconf.readthedocs.io/zh_CN/stable/tutorial-web/src/Tutorial6.html) + - [支持定时校验中心的配置 和 多客户端配置的一致性](http://disconf.readthedocs.io/zh_CN/stable/tutorial-web/src/Tutorial6.html) + +## 2.6.15 ## + +发布于:2014年11月7日 + +- disconf-client: + - [非注解式(托管式)的配置文件添加,增加额外的定义方式。原有的方式(2.6.14版本)的方式亦兼容,但不推荐使用。](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial5.html) + - [非注解式(托管式)的配置文件额外支持xml(以前仅支持properties)格式。](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial5.html) + +## 2.6.14 ## + +发布于:2014年9月18日 + +- disconf-client: + - [支持非注解方式(托管式)的配置文件统一化(只支持.propertes格式)](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial5.html) + - fix bug: 静态配置文件无法动态更新的BUG + - ZK session expire time enlarge from 5 to 30 seconds + - [支持自定义过滤分布式配置](http://disconf.readthedocs.io/zh_CN/stable/tutorial-client/src/Tutorial7.html) +- disconf-web: 支持更便捷人性化的ZK查询 + - 配置文件的输入支持 直接文本输入+上传配置文件方式 + - 支持配置文件下载,批量下载 + - 支持显示配置所影响的机器源,并提供配置数据校验工具 + - 全新Web主页 + +## 2.6.13 ## + +发布于:2014年9月4日 + +- fix bug: 配置里解析Integer(或类似非String)数据时出错 +- Zoo Preifix: client get this value from server, not from local config +- fix bug: disconf不是最高优先级启动,导致在本地没有配置文件时,PropertyPlaceholderConfigurer在Disconf启动前初始化, +location为空,因此它认为没有配置文件存在,出现Spring启动失败。 +修改方法是,使用BeanDefinitionRegistryPostProcessor使Disconf最高优先级启动,这样后面执行PropertyPlaceholderConfigurer初始化 +时就可以发现所有的配置文件。 + +## 2.6.11 & 2.6.12 ## + +- 修复BUG: 当不使用Disconf时,close会有Null异常 +- 打日志策略更新:原则上日志为Debug,出错为ERROR,需要注意为WARN + +## 2.6.10 ## + +- change log: + - 注入静态配置域时不再打印错误字段 + - 配置完成后打印配置仓库时打印方式pretty化 +- 修复BUG: 支持空配置文件类,如EmptyConf.java,可以使用它来实现简单的同步 + +## 2.6.9 ## + +- FixBug:找不到 disconf_sys.properties +- 增加功能: + - 支持静态配置文件分布式 + - 支持配置配置项分布式 + +## 2.6.8 ## + +- Init Version \ No newline at end of file diff --git a/docs/source/question/disconf-question.md b/docs/source/question/disconf-question.md new file mode 100644 index 000000000..d3b36314d --- /dev/null +++ b/docs/source/question/disconf-question.md @@ -0,0 +1,17 @@ +Client常问问题 +======= + +## **异常:** ERROR c.b.d.c.c.processor.impl.DisconfCoreProcessUtils - Spring Context is null. Cannot autowire com.szzjcs.commons.thirdapi.push.config.JpushConfig + +**可能原因:** + +- 程序没有使用spring环境 +- `` 放在 disconfMgrBean 定义的后面 +- 对于版本2.6.28(包括此版本)之前的版本, component-scan 可能没有扫描 `com.baidu.disconf` + +**解决办法:** + +- 非静态配置 必须使用spring环境 +- `` 必须出现在disconfMgrBean之前 +- 对于版本2.6.28(包括此版本)之前的版本,必须使 component-scan 增加扫描项 `com.baidu.disconf` + diff --git a/docs/source/question/disconf-web-question.md b/docs/source/question/disconf-web-question.md new file mode 100644 index 000000000..96761c5ea --- /dev/null +++ b/docs/source/question/disconf-web-question.md @@ -0,0 +1,6 @@ +Disconf-Web常问问题 +======= + +## 找不到 get/set方法 + +请使用 Lombok [https://github.com/knightliao/disconf/issues/38](https://github.com/knightliao/disconf/issues/38) \ No newline at end of file diff --git a/docs/source/question/index.rst b/docs/source/question/index.rst new file mode 100644 index 000000000..6345e1630 --- /dev/null +++ b/docs/source/question/index.rst @@ -0,0 +1,10 @@ +常问问题 +======= + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/异常考虑 + src/disconf-question + src/disconf-web-question \ No newline at end of file diff --git a/docs/source/question/src/disconf-question.rst b/docs/source/question/src/disconf-question.rst new file mode 100644 index 000000000..1e7f37ec3 --- /dev/null +++ b/docs/source/question/src/disconf-question.rst @@ -0,0 +1,19 @@ +Client常问问题 +============== + +**异常:** ERROR c.b.d.c.c.processor.impl.DisconfCoreProcessUtils - Spring Context is null. Cannot autowire com.szzjcs.commons.thirdapi.push.config.JpushConfig +-------------------------------------------------------------------------------------------------------------------------------------------------------------- + +**可能原因:** + +- 程序没有使用spring环境 +- ```` 放在 disconfMgrBean 定义的后面 +- 对于版本2.6.28(包括此版本)之前的版本, component-scan 可能没有扫描 + ``com.baidu.disconf`` + +**解决办法:** + +- 非静态配置 必须使用spring环境 +- ```` 必须出现在disconfMgrBean之前 +- 对于版本2.6.28(包括此版本)之前的版本,必须使 component-scan + 增加扫描项 ``com.baidu.disconf`` diff --git a/docs/source/question/src/disconf-web-question.rst b/docs/source/question/src/disconf-web-question.rst new file mode 100644 index 000000000..d44d8ee9d --- /dev/null +++ b/docs/source/question/src/disconf-web-question.rst @@ -0,0 +1,7 @@ +Disconf-Web常问问题 +=================== + +找不到 get/set方法 +------------------ + +请使用 Lombok https://github.com/knightliao/disconf/issues/38 diff --git "a/docs/source/question/src/\345\274\202\345\270\270\350\200\203\350\231\221.rst" "b/docs/source/question/src/\345\274\202\345\270\270\350\200\203\350\231\221.rst" new file mode 100644 index 000000000..868a96a4b --- /dev/null +++ "b/docs/source/question/src/\345\274\202\345\270\270\350\200\203\350\231\221.rst" @@ -0,0 +1,68 @@ +异常考虑 +======== + +使用Disconf-client和Disconf-Web,如果进行一些非预期操作,可能会影响到配置服务的正常运行,下面归纳了 +目前配置系统的异常处理策略: + +**disconf-web事件定义:** + +:: + + A1: 未启动disconf-web + A2: 启动Disconf-web后,未上传一个配置文件叫redis.properties + A3: 在Disconf-Web上更新分布式配置文件 + +**disconf-client事件定义:** + +:: + + B1: 启动包含了Disconf-client的实例 + B2: 启动包含了Disconf-client的实例,它需要一个redis.properties作为分布式配置 + B3: 有一个包含了Disconf-client的实例使用此分布式配置文件redis.properties + +以下为详细表格 + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作前提操作时间点操作行为配置系统的处理影响用户建议
A1B1B1此实例无法使用所有分布式配置此实例使用本地(classpath目录下)的配置启动disconf-web, 然后再重新实例
A2B1B2此实例无法使用此分布式配置redis.properties此实例使用本地的配置redis.properties先上传redis.properties, 然后重新启动实例
A2 & B3A3误操作,上传了一个空的配置文件redis.properties此实例无法正常更新配置文件redis.properties1. 此实例没有更新配置,仍沿用以前的配置
2. 实例日志打印ERROR错误
3. 监控系统报警(由于ZK上的配置数据与DB出现不同步,因此有报警)
重新上传redis.properties
A2 & B3A3误操作,上传了一个别人的配置文件remote.propertiesdisconf-web拒绝了此次更新请求上传失败重新上传redis.properties
diff --git "a/docs/source/question/\345\274\202\345\270\270\350\200\203\350\231\221.md" "b/docs/source/question/\345\274\202\345\270\270\350\200\203\350\231\221.md" new file mode 100644 index 000000000..ad94711ae --- /dev/null +++ "b/docs/source/question/\345\274\202\345\270\270\350\200\203\350\231\221.md" @@ -0,0 +1,61 @@ +异常考虑 +======= + +使用Disconf-client和Disconf-Web,如果进行一些非预期操作,可能会影响到配置服务的正常运行,下面归纳了 目前配置系统的异常处理策略: + +**disconf-web事件定义:** + + A1: 未启动disconf-web + A2: 启动Disconf-web后,未上传一个配置文件叫redis.properties + A3: 在Disconf-Web上更新分布式配置文件 + +**disconf-client事件定义:** + + B1: 启动包含了Disconf-client的实例 + B2: 启动包含了Disconf-client的实例,它需要一个redis.properties作为分布式配置 + B3: 有一个包含了Disconf-client的实例使用此分布式配置文件redis.properties + +以下为详细表格 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
操作前提操作时间点操作行为配置系统的处理影响用户建议
A1B1B1此实例无法使用所有分布式配置此实例使用本地(classpath目录下)的配置启动disconf-web, 然后再重新实例
A2B1B2此实例无法使用此分布式配置redis.properties此实例使用本地的配置redis.properties先上传redis.properties, 然后重新启动实例
A2 & B3A3误操作,上传了一个空的配置文件redis.properties此实例无法正常更新配置文件redis.properties1. 此实例没有更新配置,仍沿用以前的配置
2. 实例日志打印ERROR错误
3. 监控系统报警(由于ZK上的配置数据与DB出现不同步,因此有报警)
重新上传redis.properties
A2 & B3A3误操作,上传了一个别人的配置文件remote.propertiesdisconf-web拒绝了此次更新请求上传失败重新上传redis.properties
\ No newline at end of file diff --git a/docs/source/quick/TutorialSummary.md b/docs/source/quick/TutorialSummary.md new file mode 100644 index 000000000..adcb1c88e --- /dev/null +++ b/docs/source/quick/TutorialSummary.md @@ -0,0 +1,184 @@ +TutorialSummary 分布式配置系统功能概述 +======= + +## 托管配置 ## + +通过简单的注解类方式 托管配置。托管后,本地不需要此配置文件,统一从配置中心服务获取。 + +当配置被更新后,**注解类的数据自动同步**。 + + @Service + @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; + } + } + +[Tutorial1](../../tutorial-client/Tutorial1.html) + +## 配置更新回调 ## + +如果配置更新时,您需要的是 **不仅注解类自动同步,并且其它类也需要做些变化**,那么您需要一个回调来帮忙。 + + @Service + @Scope("singleton") + @DisconfUpdateService(classes = { JedisConfig.class }) // 这里或者写成 @DisconfUpdateService(confFileKeys = { "redis.properties" }) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate + +[Tutorial2](../../tutorial-client/Tutorial2.html) + +## 支持基于XML的配置文件托管 ## + +除了支持基于注解式的配置文件,我们还支持 基于XML无代码侵入式的: + +(properties文件更新时数据自动同步reload,非properties文件需要写回调来支持数据自动同步) + + + + + classpath:/autoconfig.properties + classpath:/autoconfig2.properties + classpath:/myserver_slave.properties + classpath:/testJson.json + classpath:/testXml2.xml + myserver.properties + + + + + + + + + + + + + + + + +[Tutorial8](../../tutorial-client/Tutorial8.html) + +## 支持配置项 ## + +变量亦支持分布式配置哦 + + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + +[Tutorial3](../../tutorial-client/Tutorial3.html) + + +## 支持静态配置 ## + +除了支持@Service类以外,我们还支持 静态配置 + + @DisconfFile(filename = "static.properties") + public class StaticConfig { + + private static int staticVar; + + @DisconfFileItem(name = "staticVar", associateField = "staticVar") + public static int getStaticVar() { + return staticVar; + } + } + +[Tutorial4](../../tutorial-client/Tutorial4.html) + +## 支持基于XML的配置文件托管: 不会自动reload ## + +与 **支持基于XML的配置文件托管** 相对应,只是在配置文件更改时,不会自动reload到java bean里。 + +值得说的是,此种方式支持 任意类型 格式配置文件。 + + + + + + myserver.properties + + + + + + + + + + + + + + + +[Tutorial5](../../tutorial-client/Tutorial5.html) + +## 过滤要进行托管的配置 ## + +有时候你不想托管所有的配置文件,有1~2个配置文件你只想和本地的,可以: + + # 忽略哪些分布式配置,用逗号分隔 + ignore=jdbc-mysql.properties + +[Tutorial7](../../tutorial-client/Tutorial7.html) + +## 强大的WEB配置平台控制 ## + +在WEB配置平台上,您可以 + +- 上传、更新 您的配置文件、配置项(有邮件通知),并且实现动态推送。 +- 批量下载配置文件,查看ZK上部署情况 +- 查看 此配置的影响范围: 哪些机器在使用,各机器上的配置内容各是什么,并且自动校验 一致性。 +- 支持 自动化校验配置一致性。 +- 简单权限控制 + +详见:[Tutorial6](../../tutorial-web/Tutorial6.html) + +![](http://ww3.sinaimg.cn/mw1024/60c9620fgw1ekdeid28pmj20rc0fkdj9.jpg) + + + + + + + diff --git a/docs/source/quick/index.rst b/docs/source/quick/index.rst new file mode 100644 index 000000000..71a5af987 --- /dev/null +++ b/docs/source/quick/index.rst @@ -0,0 +1,8 @@ +Quick Start +=========== + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/TutorialSummary diff --git a/docs/source/quick/src/TutorialSummary.rst b/docs/source/quick/src/TutorialSummary.rst new file mode 100644 index 000000000..6628f02c3 --- /dev/null +++ b/docs/source/quick/src/TutorialSummary.rst @@ -0,0 +1,205 @@ +TutorialSummary 分布式配置系统功能概述 +====================================== + +托管配置 +-------- + +通过简单的注解类方式 +托管配置。托管后,本地不需要此配置文件,统一从配置中心服务获取。 + +当配置被更新后,\ **注解类的数据自动同步**\ 。 + +:: + + @Service + @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; + } + } + +`Tutorial1 <../../tutorial-client/Tutorial1.html>`__ + +配置更新回调 +------------ + +如果配置更新时,您需要的是 +**不仅注解类自动同步,并且其它类也需要做些变化**\ ,那么您需要一个回调来帮忙。 + +:: + + @Service + @Scope("singleton") + @DisconfUpdateService(classes = { JedisConfig.class }) // 这里或者写成 @DisconfUpdateService(confFileKeys = { "redis.properties" }) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate + +`Tutorial2 <../../tutorial-client/Tutorial2.html>`__ + +支持基于XML的配置文件托管 +------------------------- + +除了支持基于注解式的配置文件,我们还支持 基于XML无代码侵入式的: + +(properties文件更新时数据自动同步reload,非properties文件需要写回调来支持数据自动同步) + +:: + + + + + classpath:/autoconfig.properties + classpath:/autoconfig2.properties + classpath:/myserver_slave.properties + classpath:/testJson.json + classpath:/testXml2.xml + myserver.properties + + + + + + + + + + + + + + + + +`Tutorial8 <../../tutorial-client/Tutorial8.html>`__ + +支持配置项 +---------- + +变量亦支持分布式配置哦 + +:: + + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + +`Tutorial3 <../../tutorial-client/Tutorial3.html>`__ + +支持静态配置 +------------ + +`除了支持@Service类以外 `__\ ,我们还支持 +静态配置 + +:: + + @DisconfFile(filename = "static.properties") + public class StaticConfig { + + private static int staticVar; + + @DisconfFileItem(name = "staticVar", associateField = "staticVar") + public static int getStaticVar() { + return staticVar; + } + } + +`Tutorial4 <../../tutorial-client/Tutorial4.html>`__ + +支持基于XML的配置文件托管: 不会自动reload +----------------------------------------- + +与 **支持基于XML的配置文件托管** +相对应,只是在配置文件更改时,不会自动reload到java bean里。 + +值得说的是,此种方式支持 任意类型 格式配置文件。 + +:: + + + + + + myserver.properties + + + + + + + + + + + + + + +`Tutorial5 <../../tutorial-client/Tutorial5.html>`__ + +过滤要进行托管的配置 +-------------------- + +有时候你不想托管所有的配置文件,有1~2个配置文件你只想和本地的,可以: + +:: + + # 忽略哪些分布式配置,用逗号分隔 + ignore=jdbc-mysql.properties + +`Tutorial7 <../../tutorial-client/Tutorial7.html>`__ + +强大的WEB配置平台控制 +--------------------- + +在WEB配置平台上,您可以 + +- 上传、更新 您的配置文件、配置项(有邮件通知),并且实现动态推送。 +- 批量下载配置文件,查看ZK上部署情况 +- 查看 此配置的影响范围: + 哪些机器在使用,各机器上的配置内容各是什么,并且自动校验 一致性。 +- 支持 自动化校验配置一致性。 +- 简单权限控制 + +详见:\ `Tutorial6 <../../tutorial-web/Tutorial6.html>`__ + +|image0| + +.. |image0| image:: http://ww3.sinaimg.cn/mw1024/60c9620fgw1ekdeid28pmj20rc0fkdj9.jpg + diff --git a/docs/source/tutorial-client/Tutorial1.md b/docs/source/tutorial-client/Tutorial1.md new file mode 100644 index 000000000..a542973f8 --- /dev/null +++ b/docs/source/tutorial-client/Tutorial1.md @@ -0,0 +1,458 @@ +Tutorial 1 注解式分布式的配置文件(最佳实践) +======= + +这里以 disconf-demo/disconf-standalone-demo 某个程序片段为例,详细介绍了一个 分布式配置文件 的简单示例程序。 + +假设,我们的应用程序使用了Redis服务,我们将使用Jedis来进行编程。编程时,我们需要Redis的Host和Port,通常情况下,我们会把这两个参数放在配置文件里。 + +**本教程将以两个部分来进行**, + +- 第一部分讲解正常情况下(不使用Disconf)的写法,这是我们以前常做的事情 。 +- 第二部分,会在第一部分的基础上,添加Disconf的支持。从这一部分,大家就可以看到Disconf的简洁性和低侵入性。 +并且,大家也可以看到关闭和开启Disconf,原有程序(第一部分)都可以正确Work。 + +## 第一部分:一个简单普通的Redis程序 + + +### 第一步:准备一个配置文件 redis.properties + +我们需要一个 redis.properties 文件,里面含有 Host 和 Port。文件内容是: + + redis.host=127.0.0.1 + redis.port=6379 + +我们需要把此文件放在项目的ClassPath路径下。 + +### 第二步:撰写配置文件相应的配置文件类 + +我们撰写一个类JedisConfig,它与 redis.properties 相对应。整个类的完整代码如下: + + package com.example.disconf.demo.config; + + import org.springframework.stereotype.Service; + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + public class JedisConfig { + + // 代表连接地址 + private String host; + + // 代表连接port + private int port; + + /** + * 地址 + * + * @return + */ + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + /** + * 端口 + * + * @return + */ + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + } + +注意,这里的Get&Set方法均是Eclipse自动生成的。 + +在applicationContext.xml 添加以下代码,目的是将配置值注入到此类中: + + + + + + + + + classpath*:/redis.properties + + + + + + + + + +### 第三步:一个简单的Redis服务程序 + +我们的初衷是使用Redis服务。因此,我们需要撰写一个连接Redis的Service类,它使用的是第二步里的配置文件类。完整类的实现代码如下: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.DisposableBean; + import org.springframework.beans.factory.InitializingBean; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Service; + + import redis.clients.jedis.Jedis; + + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.utils.JedisUtil; + + /** + * 一个简单的Redis服务 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + public class SimpleRedisService implements InitializingBean, DisposableBean { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(SimpleRedisService.class); + + // jedis 实例 + private Jedis jedis = null; + + /** + * 分布式配置 + */ + @Autowired + private JedisConfig jedisConfig; + + /** + * 关闭 + */ + public void destroy() throws Exception { + + if (jedis != null) { + jedis.disconnect(); + } + } + + /** + * 进行连接 + */ + public void afterPropertiesSet() throws Exception { + + jedis = JedisUtil.createJedis(jedisConfig.getHost(), + jedisConfig.getPort()); + } + + /** + * 获取一个值 + * + * @param key + * @return + */ + public String getKey(String key) { + if (jedis != null) { + return jedis.get(key); + } + + return null; + } + } + + +**具体步骤是:** + +1. 此类实现了 InitializingBean, DisposableBean 两个接口,目的是在Bean初始化后进行Redis的连接。 +2. 为此类添加 @Service ,代表它是一个Bean。Spring托管的,且 "scope" 都必须是singleton的。 + +### 第四步:使用SimpleRedisService + +使用起来非常简单, 示例如下: + + package com.example.disconf.demo.task; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Service; + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.service.SimpleRedisService; + + /** + * 演示分布式配置文件、分布式配置的更新Demo + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + public class DisconfDemoTask { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(DisconfDemoTask.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + @Autowired + private JedisConfig jedisConfig; + + private static final String REDIS_KEY = "disconf_key"; + + /** + * + */ + public int run() { + + try { + + while (true) { + + Thread.sleep(5000); + + LOGGER.info("redis( " + jedisConfig.getHost() + "," + + jedisConfig.getPort() + ") get key: " + REDIS_KEY + + } + + } catch (Exception e) { + + LOGGER.error(e.toString(), e); + } + + return 0; + } + } + + + +## 第二部分:支持分布式配置(disconf)的简单Redis程序 + +### 第一步:添加Disconf的支持 + +在applicationContext.xml里添加Bean定义: + + + + + + + + +其中这里,我们定义 属性“scanPackage” 的值是 com.example.disconf.demo。 +这里需要填上你的项目的Package名。这与Spring的auto scan包名功能一样。 + +另外,从2.6.23起,这里的 `scanPackage` 属性支持扫描多包,逗号分隔,例如: + + + + + +### 第二步 项目准备 + +####修改扫描类 + +你的项目的扫描类是com.example,为了支持disconf,因此,必须添加扫描类 com.baidu ,如: + + + +注:从版本`2.6.30`开始,不再需要扫描包`com.baidu`了,扫描自己的包即可。即: + + + +####支持 cglib aop + +使你的项目支持 cglib的aop + + + +### 第三步:修改JedisConfig支持分布式配置 + +我们撰写一个类JedisConfig,它与 redis.properties 相对应。整个类的完整代码如下: + + package com.example.disconf.demo.config; + + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @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; + } + } + +**具体步骤是:** + +1. 为这个类 JedisConfig 定义 @DisconfFile 注解,必须指定文件名为 redis.properties 。 +2. 定义域 host port,分别表示Host和Port。并使用Eclipse为其自动生成 get&set 方法。 +3. 为这两个域的get方法上添加注解 @DisconfFileItem 。添加标记 name, 表示配置文件中的KEY名,这是必填的。标记associateField是可选的,它表示此get方法相关连的域的名字,如果此标记未填,则系统会自动分析get方法,猜测其相对应于域名。强烈建议添加associateField标记,这样就可以避免Eclipse生成的Get/Set方法不符合Java规范的问题。 +4. 标记它为Spring托管的类 (使用@Service),且 "scope" 都必须是singleton的。 + +**注意:** + +Eclipse自动生成的get方法,可能与Java的规范不同。这会导致很多问题。因此,建议加上 associateField 标记。 + +### 第四步:添加 disconf.properties + +**准备disconf.properties文件:** + +Disconf启动需要此文件,文件示例是: + + # 是否使用远程配置文件 + # true(默认)会从远程获取配置 false则直接获取本地配置 + enable.remote.conf=true + + # + # 配置服务器的 HOST,用逗号分隔 127.0.0.1:8000,127.0.0.1:8000 + # + conf_server_host=127.0.0.1:8080 + + # 版本, 请采用 X_X_X_X 格式 + version=1_0_0_0 + + # APP 请采用 产品线_服务名 格式 + app=disconf_demo + + # 环境 + env=rd + + # debug + debug=true + + # 忽略哪些分布式配置,用逗号分隔 + ignore= + + # 获取远程配置 重试次数,默认是3次 + conf_server_url_retry_times=1 + # 获取远程配置 重试时休眠时间,默认是5秒 + conf_server_url_retry_sleep_seconds=1 + +配置相关说明可参考:[配置](../config/client-config.html) + +注意:如果使用Disconf,则本地的配置文件`redis.properties`可以删除掉(也可以不删除掉,建议删除掉)。如果不使用Disconf,则需要此配置文件。 + +### 第五步:在`disconf-web`上上传配置文件(`redis.properties`) + +当你的程序启动时,disconf就会帮忙你的程序去获取配置文件。那如何让disconf知道你的配置呢?答案是需要在disconf-web上传配置文件哦。 + +点击主页面的新建配置文件按钮: + +![](http://ww3.sinaimg.cn/mw1024/60c9620fjw1em9mgdgkn8j20ti04x3za.jpg) + +进入页面后就可以上传 配置文件了 + +![](http://ww1.sinaimg.cn/mw1024/60c9620fjw1em9mkivysij20q20fk0tm.jpg) + +### 第六步:在`disconf-web`上查看 + +你在第五步上传了配置文件 redis.properties ,那么 ,当你的程序启动时,disconf就会帮忙你的程序去获取配置文件。 + +可以看到已经有一个实例在使用redis.properties了。 + +![](http://ww4.sinaimg.cn/mw1024/60c9620fgw1ekdf6ymx9yj20rc0ey0w3.jpg) + +点击查看它的详情,可以看到,确实是我的实例在使用它。 + +![](http://ww1.sinaimg.cn/mw1024/60c9620fgw1ekdf8i229bj20rt0fotbt.jpg) + +### 完结 ## + +至此,分布式配置文件的撰写就已经写完了。 + +可以看到,基于注解的方式,不需要在xml定义 java bean(config类). + +#### 使用方便 + +大家可以看到,第一次使用时,需要 + +- 在`applicationContext.xml`添加Disconf启动支持 +- 使用注解方式 修改配置类 +- 添加`disconf.properties` +- 在`disconf-web`上上传配置文件 + +非第一次使用时,需要 + +- 使用注解方式 修改配置类 +- 在`disconf-web`上上传配置文件 + +就可以支持分布式配置了。 + +#### 强兼容性 + +并且,如果将 `disconf.disconf.properties` 中的 `enable.remote.conf` 设置为 `false` + +那么,分布式配置就会失效,退化为 使用本地配置方式(即第一部分的功能)。(如果是这种情况,你必须确认你本地留有相应的配置文件。 +一般来说,只要成功跑过一次基于disconf的程序,那么classpath目录下就会有此程序的所有相应配置文件。) + +并且,如果 disconf-web无法正常服务(`conf_server_host=127.0.0.1:8080`),分布式配置也会失效,退化为 使用本地配置方式(即第一部分的功能)。(如果是这种情况,你必须确认你本地留有相应的配置文件) + +也就是说,Disconf是具有兼容性的 + +- 当开启Disconf时, + - 如果Disconf正常运行,则正常使用分布式配置。 + - 如果Disconf非正常运行,则使用本地配置。(Disconf可以保证在Disconf失败时,原有程序能够按原有逻辑正确运行) +- 当不开启Disconf时, 则使用本地配置。 + +**注:** + +1. 只要是运行一次分布式程序成功,则本地就含有最全的配置文件。此时,如果再运行一次分布式程序,如果出现失败,则上一次下载成功的配置文件就会当成本地配置生效,程序成功启动。 + + +## END + diff --git a/docs/source/tutorial-client/Tutorial10.md b/docs/source/tutorial-client/Tutorial10.md new file mode 100644 index 000000000..88be297de --- /dev/null +++ b/docs/source/tutorial-client/Tutorial10.md @@ -0,0 +1,16 @@ +Tutorial 10 实现一个配置更新下载器agent +======= + +### 问题 + +我想在我的机器上做一个配置下载器agent, 可以实现以下功能: + +- 启动时下载配置 +- 配置被更新时,可以感知并下载下来 + +### 解决方法 + +可以修改一下 disconf-demos/disconf-standalone-demo 这个项目,让其变成一个 长驻进程,并指定 +[disconf.user_define_download_dir](../../config/client-config.html) 这个配置到你想指定的路径。 + +done. diff --git a/docs/source/tutorial-client/Tutorial11-config-download-path.md b/docs/source/tutorial-client/Tutorial11-config-download-path.md new file mode 100644 index 000000000..2d0e866de --- /dev/null +++ b/docs/source/tutorial-client/Tutorial11-config-download-path.md @@ -0,0 +1,32 @@ +Tutorial 11 配置文件下载地址讨论 +======= + +### 问题一 从disconf下载的配置文件都放到哪里去了? + +### 解决:按以下顺序进行判断 + +对于注解式配置文件: + +- 一定会下载到 disconf.user_define_download_dir 目录下(使用此方法可以方便的构造一个下载器. [Tutorial10](Tutorial10.html) ) +- 如果 disconf.enable_local_download_dir_in_class_path 为true(默认值), 则会执行以下判断: + - 如果 @DisconfFile 的 targetDirPath 值不为空, 则会下载到 targetDirPath 这个目录下, 配置数据从该路径读取。这对于那些不想放在classpath根目录的程序, 比较有用. + - 如果 @DisconfFile 的 targetDirPath 值为空, 则会下载到classpath路径下, 配置数据从该路径读取. +- 如果 disconf.enable_local_download_dir_in_class_path 为false, 则不会下载到classpath目录下. 配置数据从 disconf.user_define_download_dir 读取 + +对于XML式配置文件: + +- 一定会下载到 disconf.user_define_download_dir 目录下(使用此方法可以方便的构造一个下载器). +- 如果 disconf.enable_local_download_dir_in_class_path 为true(默认值), 则会执行以下判断: + - 如果 @DisconfFile 的 targetDirPath 值不为空, 则会下载到 targetDirPath 这个目录下. + - 如果 @DisconfFile 的 targetDirPath 值为空, 则会下载到classpath路径下. +- 如果 disconf.enable_local_download_dir_in_class_path 为false, 则不会下载到classpath目录下. + +注: + +1. 如果不作任何配置的改变,默认情况下,会下载到 disconf.user_define_download_dir 目录 和 classpath 两个目录下。 +2. targetDirPath 值说明:以"/"开头则是系统的全路径,否则则是相对于classpath的路径,默认是classpath根路径。注意:根路径要注意是否有权限,否则会出现找不到路径,推荐采用相对路径。 +3. 配置说明看这里 [config](../../config/client-config.html) + +### 问题二 不想下载到classpath目录下 + +将 disconf.enable_local_download_dir_in_class_path 为false, 并 指定 下载目录 disconf.user_define_download_dir \ No newline at end of file diff --git a/docs/source/tutorial-client/Tutorial13-unify-notify.md b/docs/source/tutorial-client/Tutorial13-unify-notify.md new file mode 100644 index 000000000..4e034253c --- /dev/null +++ b/docs/source/tutorial-client/Tutorial13-unify-notify.md @@ -0,0 +1,40 @@ +Tutorial 13 增加统一的回调类 (unify-notify模式) 灵活处理更新配置通知 +======= + +### 目的 + +当 任意的 配置文件 或 配置项 得到更新时,此类 就会被调用。 + +它与 [Tutorial2](Tutorial2.html) 不一样,不需要注解,不需要必须指定变更地象。更加freely,方便大家在这里统一的、自由的控制更新逻辑. + +### 示例项目 + +https://github.com/knightliao/disconf-demos-java/tree/master/disconf-standalone-demo + +### demo + +只要实现 `IDisconfUpdatePipeline` 接口即可。不要求必须是 java bean. + +- 函数 `reloadDisconfFile` 是针对分布式配置文件的。key是文件名;filePath是文件路径。用户可以在这里(read file freely)按你喜欢的解析文件的方式进行处理。 +- 函数 `reloadDisconfItem` 是针对分布式配置项的。key是配置项名;content是其值,并且含有类型信息。 + +示例代码: + + /** + */ + @Service + public class UpdatePipelineCallback implements IDisconfUpdatePipeline { + + public void reloadDisconfFile(String key, String filePath) throws Exception { + System.out.println(key + " : " + filePath); + } + + public void reloadDisconfItem(String key, Object content) throws Exception { + System.out.println(key + " : " + content); + } + } + + + + + diff --git a/docs/source/tutorial-client/Tutorial14-bean-setter-mode.md b/docs/source/tutorial-client/Tutorial14-bean-setter-mode.md new file mode 100644 index 000000000..73b8935a1 --- /dev/null +++ b/docs/source/tutorial-client/Tutorial14-bean-setter-mode.md @@ -0,0 +1,211 @@ +Tutorial 14 配置初始化 或 更新时,通知采用 "bean setter模式" +======= + +### 目的 + +当通过 java bean 进行控制配置时,当某个配置有初始化或更新时,可以在 bean property setter 方法里 做适合自己的业务逻辑。 + +### 示例项目 + +https://github.com/knightliao/disconf-demos-java/tree/master/disconf-standalone-demo + +### demo1: java 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; + LOGGER.info("i' m here: setting redis port"); + } + + public void reload() throws Exception { + LOGGER.info("host: " + host); + } + } + +在这里的 setPort 方法 会在该 javabean `初始化` 或者 `配置更新` 时 被调用 。 + + public void setPort(int port) { + this.port = port; + LOGGER.info("i' m here: setting redis port"); + } + + +### demo2: static class 注解式配置 + + /** + */ + @DisconfFile(filename = "static.properties") + public class StaticConfig { + + protected static final Logger LOGGER = LoggerFactory.getLogger(StaticConfig.class); + + private static int staticVar; + + @DisconfFileItem(name = "staticVar", associateField = "staticVar") + public static int getStaticVar() { + return staticVar; + } + + public static void setStaticVar(int staticVar) { + StaticConfig.staticVar = staticVar; + LOGGER.info("i' m here: setting static class variable"); + } + + } + +在这里的 setStaticVar 方法 会在该 class `初始化` 或者 `配置更新` 时 被调用 。 + +### demo3: 基于XML配置文件的无侵入式 配置 + +class: + + /** + */ + public class AutoService { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AutoService.class); + + private String auto; + + public String getAuto() { + return auto; + } + + public void setAuto(String auto) { + this.auto = auto; + LOGGER.info("i' m here: setting auto"); + } + } + +配置: + + + + + + classpath:/autoconfig.properties + + + + + + + + + + + + + + +### demo4: 配置项 配置 + + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory.getLogger(BaoBaoService.class); + + public static final String key = "moneyInvest"; + + @Value(value = "2000d") + private Double moneyInvest; + + @Autowired + private Coefficients coefficients; + + /** + * 计算百发一天赚多少钱 + * + * @return + */ + public double calcBaiFa() { + return coefficients.getBaiFaCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * k 计算余额宝一天赚多少钱 + * + * @return + */ + public double calcYuErBao() { + return coefficients.getYuErBaoCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * 投资的钱,分布式配置
+ *
+ * 这里切面无法生效,因为SpringAOP不支持。
+ * 但是这里还是正确的,因为我们会将值注入到Bean的值里. + * + * @return + */ + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + + public void setMoneyInvest(Double moneyInvest) { + this.moneyInvest = moneyInvest; + LOGGER.info("i' m here: setting moneyInvest"); + } + + } + +在这里的 setMoneyInvest 方法 会在该 class `初始化` 或者 `配置更新` 时 被调用 。 + +### 配置更新时通知的所有方式 总结 + +- 指定key的注解式 [Tutorial2](Tutorial2) +- 统一通知模式 unify-notify [Tutorial13](Tutorial13-unify-notify) +- bean setter模式 + + + + + + + + + + diff --git a/docs/source/tutorial-client/Tutorial2.md b/docs/source/tutorial-client/Tutorial2.md new file mode 100644 index 000000000..6f3ce82fa --- /dev/null +++ b/docs/source/tutorial-client/Tutorial2.md @@ -0,0 +1,238 @@ +Tutorial 2 注解式分布式的配置文件高级篇: 配置更新的通知(最佳实践) +======= + +在 [Tutorial 1](Tutorial1.html) 里, +我们实现了一个简单的Redis服务程序,它使用分布式配置进行管理,此Redis的配置文件存储在分布式服务器 disconf-web 上。 + +也许有一天,我们需要更新Redis的Host和Port配置数据。由于 redis是根据配置生成的实例,因此,这种情况下,你有三种选择: + +- 不使用Disconf([Tutorial 1](Tutorial1.html) 里第一部分的使用方法)。那么你需要 更改线上机器的配置文件 redis.properties,重启服务就可以了。 +- 使用Disconf, 采用 [Tutorial 1](Tutorial1.html) 里第二部分的使用的方案。那么你需要 更改 分布式服务器 disconf-web 上的 redis.properties 文件。 +然后重启服务就可以了。和第一种方法的区别在于,不需要更改线上机器的配置文件。 +- 使用Disconf,采用 [Tutorial 1](Tutorial1.html) 里第二部分的使用的方案,并额外加上本Tutorial的方案,那么你 只需要 更改 分布式服务器 disconf-web 上的 redis +.properties 文件。然后服务的配置自动生效。此过程无需要重新启动服务。 + +本教程就是阐述如何通过简单的配置和极简代码实现第三步的功能。 + +## 第一步:准备工作 + +完成 [Tutorial 1](Tutorial1.html) 上第二部分方案里的步骤。 + +## 第二步:修改 SimpleRedisService,支持Redis重连接 + +在这里,我们这个类添加了一个方法,重新生成了一个Jedis对象,代码如下: + + /** + * 更改Jedis + */ + 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."); + } + +之所以添加这个函数的原因是:在配置更新时,此函数要被调用,从而更改Jedis实例。 + +整个类的完整代码如下: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.DisposableBean; + import org.springframework.beans.factory.InitializingBean; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.utils.JedisUtil; + + import redis.clients.jedis.Jedis; + + /** + * 一个简单的Redis服务 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + public class SimpleRedisService implements InitializingBean, DisposableBean { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisService.class); + + // jedis 实例 + private Jedis jedis = null; + + /** + * 分布式配置 + */ + @Autowired + private JedisConfig jedisConfig; + + /** + * 关闭 + */ + public void destroy() throws Exception { + + if (jedis != null) { + jedis.disconnect(); + } + } + + /** + * 进行连接 + */ + public void afterPropertiesSet() throws Exception { + + jedis = JedisUtil.createJedis(jedisConfig.getHost(), jedisConfig.getPort()); + } + + /** + * 获取一个值 + * + * @param key + * + * @return + */ + public String getKey(String key) { + if (jedis != null) { + return jedis.get(key); + } + + return null; + } + + /** + * 更改Jedis + */ + 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."); + } + } + +## 第三步: 撰写配置更新回调类 + +当配置更新时,应用程序要得到通知。因此我们要写一个回调类来响应此“通知”。完整的类如下: + + package com.example.disconf.demo.service.callbacks; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfUpdateService; + import com.baidu.disconf.client.common.update.IDisconfUpdate; + import com.example.disconf.demo.config.Coefficients; + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.service.SimpleRedisService; + + /** + * 更新Redis配置时的回调函数 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + @DisconfUpdateService(classes = {JedisConfig.class}, itemKeys = {Coefficients.key}) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisServiceUpdateCallback.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + /** + * + */ + public void reload() throws Exception { + + simpleRedisService.changeJedis(); + } + + } + +**具体步骤是:** + +- 撰写此类,实现 IDisconfUpdate 接口。此类必须是JavaBean,Spring托管的,且 "scope" 都必须是singleton的。 +- 添加 @DisconfUpdateService 注解,classes 值加上 JedisConfig.class ,表示当 JedisConfig.class 这个配置文件更新时,此回调类将会被调用。或者,使用 +confFileKeys 也可以。 +- 在函数 reload() 里调用 SimpleRedisService 的 changeJedis() 方法 + +### 回调类与配置类放在一起 + +如果你觉得写两个类太累,在某些场景下,则可以将回调与配置类放在一起的。 + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @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 { + LOGGER.info("host: " + host); + } + } + +## 完结 + +至此,支持配置更新的 分布式配置文件 的撰写就已经写完了。 + +当用户在 disconf-web 上更新配置时,你的服务里的Redis就会指向新的地址。 \ No newline at end of file diff --git a/docs/source/tutorial-client/Tutorial3.md b/docs/source/tutorial-client/Tutorial3.md new file mode 100644 index 000000000..9c3b84822 --- /dev/null +++ b/docs/source/tutorial-client/Tutorial3.md @@ -0,0 +1,273 @@ +Tutorial 3 注解式分布式的配置项(最佳实践) +======= + +在 [Tutorial 1](Tutorial1.html) 里, +我们实现了一个简单的Redis服务程序,它使用注解式的分布式配置进行管理,此Redis的配置文件存储在分布式服务器 disconf-web 上。 + +那如果你的配置只是一个变量,不是配置文件,怎么办?还能实现分布式配置吗? + +答案当然是肯定的! + +Disconf支持配置项(配置项是指 一个类的某个域变量)的分布式化。 + +这里以 disconf-demo 某个程序片段为例,详细介绍了 分布式的配置项 的简单示例程序。 + +在这里,我们将分两种情况来进行演示: + +1. 配置项在某个配置类里:外部程序通过配置类的get`***`方法获取。 +2. 配置项在某个Service类里。Service通过本身类的的get`***`方法获取。 + +对于这两种方式,Disconf的后台实现方式上有所不同。这里不会讲述具体原因。但是Disconf做到了兼容性,以上两种方式均支持。 + +## 配置类里的配置项 ## + +### 第一步:撰写 配置项类 ### + +这里假设有一个金融系数类,它有一个折扣率变量。 + + package com.example.disconf.demo.config; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + import com.baidu.disconf.client.common.annotations.DisconfItem; + + + /** + * 金融系数文件 + * + **/ + @Service + @DisconfFile(filename = "coefficients.properties") + 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; + } + } + + +**具体步骤是:** + +1. 编写Coefficients类,添加域 discount +2. 用Eclipse为域discount添加 get & set方法 +3. 为get方法添加注解 @DisconfItem(key = key) ,这里的key是分布式配置项的标识,这里是 discountRate +4. 此类必须是JavaBean,Spring托管的,且 "scope" 都必须是singleton的。 +5. 可以使用@Value为它设置一个默认值。 + +### 第二步:使用此分布式配置项 ### + +撰写一个Service类,它使用 第二步里的 discountRate 变量,完整的类是: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.Coefficients; + + /** + * 金融宝服务,计算一天赚多少钱 + * + * @author liaoqiqi + * @version 2014-5-16 + */ + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(BaoBaoService.class); + + @Autowired + private Coefficients coefficients; + + /** + * + * + * @return + */ + public double calcMoney() { + return 10000 * coefficients.getDiscount(); + } + + } + +calcMoney()会调用 coefficients.getDiscount() 获取折扣率 来计算 真正的money. + +### 第三步:配置项更新回调类 ### + +当配置项更新时,你的服务程序自动就会获取最新的配置项数据(不需要写回调函数,因为这里不像Redis这种较“重”的服务,这里的配置是实时生效的)。 + +但是,如果当你的配置项更新时,配置项本身被更新后,可能还会其它类依赖此配置项的更新,那么,你需要撰写一个回调类来获取此通知。 + +为了简单,这里我们以 [Tutorial 2](Tutorial2.html) 里的 SimpleRedisServiceUpdateCallback 类 为基础,进行扩展。 + +假设,当此配置项被更新时,Redis也需要重新被reload,那么,你可以这样来改写 SimpleRedisServiceUpdateCallback 类,完整的代码如下: + + package com.example.disconf.demo.service.callbacks; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfUpdateService; + import com.baidu.disconf.client.common.update.IDisconfUpdate; + import com.example.disconf.demo.config.Coefficients; + import com.example.disconf.demo.config.JedisConfig; + + /** + * 更新Redis配置时的回调函数 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @DisconfUpdateService(classes = { JedisConfig.class }, itemKeys = { Coefficients.key }) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(SimpleRedisServiceUpdateCallback.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + /** + * + */ + public void reload() throws Exception { + + simpleRedisService.changeJedis(); + } + + } + +这里通过为注解 @DisconfUpdateService 添加一个 itemKeys: Coefficients.key ,就实现了配置项更新的通知。怎么样?是不是很强大? + +### 第四步:在`disconf-web`上上传配置 ### + +上传方式是先在首页点击 新建配置项 + +![](http://ww3.sinaimg.cn/mw1024/60c9620fjw1em9mstdddrj20ts04ojru.jpg) + +然后新建就行啦 + +![](http://ww4.sinaimg.cn/mw1024/60c9620fjw1em9mutbw50j20q00fbdgk.jpg) + +### 完结 ### + +通过几行简单的配置,分布式配置项 就这样添加到你的应用程序里了。 + +## Service类的配置项 ## + +在上一节里,我们阐述了如何在 配置项类 里添加一个配置项的方法。 + +在本节里,我们将在上一部分的基础上,阐述如何实现 不创建配置项类 就可以 实现 分布式配置项 的方法。 + +### 准备 ### + +在 分布式配置服务器 disconf-web 上添加 moneyInvest 和 discountRate 配置项值。 + +### 第一步:撰写 含有 配置项 的Service类 ### + +在上一节里,我们撰写了一个 Coefficients.java 类,它含有 分布式配置项 discountRate,BaoBaoService.java 则是一个使用 discountRate 的服务。BaoBaoService.java 在计算(calcMoney)时,使用了固定值 10000. + +在本节里,我们 将 10000 这个值动态化,标注为分布式配置项。 + +完整的类是: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.Coefficients; + + /** + * + * @author liaoqiqi + * @version 2014-5-16 + */ + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(BaoBaoService.class); + + public static final String key = "moneyInvest"; + + private Double moneyInvest = 1000d; + + @Autowired + private Coefficients coefficients; + + /** + * + * @return + */ + public double calcMoney() { + return coefficients.getDiscount() + * getMoneyInvest(); + } + + /** + * 投资的钱,分布式配置
+ *
+ * 这里切面无法生效,因为SpringAOP不支持。
+ * 但是这里还是正确的,因为我们会将值注入到Bean的值里. + * + * @return + */ + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + + public void setMoneyInvest(Double moneyInvest) { + this.moneyInvest = moneyInvest; + } + + } + +**具体实现步骤是:** + +1. 添加域 moneyInvest ,并使用Eclipse自动生成 get&set 方法。 +2. 为get方法添加 @DisconfItem 注解,并添加 key 为 moneyInvest +3. 在函数 calcMoney() 里 调用本身类的 getMoneyInvest() 方法。 +4. 此类必须是JavaBean,Spring托管的,且 "scope" 都必须是singleton的。 + +### 第二步:在`disconf-web`上上传配置 ### + +这里不再赘述。 + +### 完结 ### + +只需要上面一步,就完成了分布式配置项。 + +配置更新也是实时的,不需要写回调函数。 + +service类的配置项 其实和 配置项类的配置项 撰写方法 差不多。它的好处是不需要再新建一个配置项类。 diff --git a/docs/source/tutorial-client/Tutorial4.md b/docs/source/tutorial-client/Tutorial4.md new file mode 100644 index 000000000..53a6485f6 --- /dev/null +++ b/docs/source/tutorial-client/Tutorial4.md @@ -0,0 +1,113 @@ +Tutorial 4 注解式分布式静态配置文件和静态配置项(最佳实践) +======= + +## 配置类 ## + +### 定义 ### + + package com.example.disconf.demo.config; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + + /** + * 静态 配置文件 示例 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @DisconfFile(filename = "static.properties") + public class StaticConfig { + + private static int staticVar; + + @DisconfFileItem(name = "staticVar", associateField = "staticVar") + public static int getStaticVar() { + return staticVar; + } + + public static void setStaticVar(int staticVar) { + StaticConfig.staticVar = staticVar; + } + + } + +### 使用 ### + + package com.example.disconf.demo.service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.StaticConfig; + + /** + * 使用静态配置文件的示例
+ * Plus
+ * 静态配置项 使用示例 + * + * @author liaoqiqi + * @version 2014-8-14 + */ + public class SimpleStaticService { + + private static int staticItem = 56; + + /** + * + * @return + */ + public static int getStaticFileData() { + + return StaticConfig.getStaticVar(); + } + } + +和 + + LOGGER.info("static file data:" + + SimpleStaticService.getStaticFileData()); + +## 配置项 ## + +###定义#### + + package com.example.disconf.demo.service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.StaticConfig; + + /** + * 使用静态配置文件的示例
+ * Plus
+ * 静态配置项 使用示例 + * + * @author liaoqiqi + * @version 2014-8-14 + */ + public class SimpleStaticService { + + private static int staticItem = 56; + + /** + * + * @return + */ + public static int getStaticFileData() { + + return StaticConfig.getStaticVar(); + } + + @DisconfItem(key = "staticItem") + public static int getStaticItem() { + return staticItem; + } + + public static void setStaticItem(int staticItem) { + SimpleStaticService.staticItem = staticItem; + } + } + + +###使用### + + LOGGER.info("static item data:" + + SimpleStaticService.getStaticItem()); \ No newline at end of file diff --git a/docs/source/tutorial-client/Tutorial5.md b/docs/source/tutorial-client/Tutorial5.md new file mode 100644 index 000000000..76d469e3a --- /dev/null +++ b/docs/source/tutorial-client/Tutorial5.md @@ -0,0 +1,40 @@ +Tutorial 5 基于XML的分布式配置文件管理,不会自动reload +======= + +在 [Tutorial 1](Tutorial1.html) 里, 我们实现了一个简单的Redis服务程序,它使用分布式配置进行管理,此Redis的配置文件存储在分布式服务器 disconf-web 上。它使用的是注解式的配置管理。 + +Disconf亦支持非注解式的分布式配置管理,下面定义一下概念: + +- 非注解式(托管式):配置文件没有相应的配置注解类,此配置文件不会被注入到配置类中。disconf只是简单的对其进行“托管”。 +启动时下载配置文件;配置文件变化时,负责动态推送。注意,此种方式,程序不会自动reload配置,需要自己写回调函数。 + +**由于此方法不会自动reload,因此,对于那些比较重的资源,比如jdbc等,是比较好的托管方式。** + +使用方法就是在你的applicationContext.xml里添加以下一段代码: + + + + + + myserver.properties + + + + + + + + + + + + + + +该配置文件列表将被Disconf全部托管。实例启动时,这些配置文件将被下载,当配置更改时,实例亦能感知到,并重新下载这些配置,并且自动调用回调函数。 + +目前支持 任意类型的 配置文件的托管。 + +注意:这些配置文件没有相应的配置文件类。 \ No newline at end of file diff --git a/docs/source/tutorial-client/Tutorial7.md b/docs/source/tutorial-client/Tutorial7.md new file mode 100644 index 000000000..fb7677505 --- /dev/null +++ b/docs/source/tutorial-client/Tutorial7.md @@ -0,0 +1,15 @@ +Tutorial 7 可自定义的部分托管的分布式配置 +======= + +假设我们已经将所有配置文件和配置项都使用Disconf进行托管了。 + +在多人开发情况下,可能会有一两个配置文件需要经常改动,且每个人的配置都不大一样,在这种情况下,当然希望此配置文件(或多个配置) +均不要使用Disconf托管。 + +Disconf考虑到了此种情况。举个实例,数据库配置文件,每个人的数据库可能不大一样,那么,你可以修改 disconf.properties : + + # 忽略哪些分布式配置,用逗号分隔 + disconf.ignore=jdbc-mysql.properties + +将此配置文件添加到ignore的列表里。这样,程序运行时,Disconf就会忽略托管此配置文件,而改为读取你本地的配置文件 jdbc-mysql.properties。 + diff --git a/docs/source/tutorial-client/Tutorial8.md b/docs/source/tutorial-client/Tutorial8.md new file mode 100644 index 000000000..9a094b557 --- /dev/null +++ b/docs/source/tutorial-client/Tutorial8.md @@ -0,0 +1,152 @@ +Tutorial 8 基于XML的分布式配置文件管理,自动reload +======= + +在 [Tutorial1](Tutorial1.html), [Tutorial2](Tutorial2.html), [Tutorial3](Tutorial3.html), [Tutorial4](Tutorial4.html) 里讲到使用 注解式的分布式配置,它的特点是 + +- 优点: + - 支持.properties配置文件 + - 支持配置项 + - 通过撰写配置类,代码结构清晰 + - 配置更新时,自动注入 + - 支持并发时配置更新统一生效 + - 无额外的XML配置,不需要在xml定义 java bean(config类) + - 代码风格 简单易懂 +- 缺点: + - 需要撰写配置类,代码侵入 + +在 [Tutorial5](Tutorial5.html) 里讲解了非注解式的分布式配置文件动态管理。它的特点是 + +- 优点: + - 支持任意类型的配置文件 + - 无代码侵入 +- 缺点: + - 需要在xml定义 java bean + - 配置更新时无法自动注入java bean里。你可以写回调函数来支持自动注入。 + +在本教程里,将讲解一种基于XML的分布式配置文件管理,它是[Tutorial5](Tutorial5.html)方式的一种增强,它的特点是: + +- 优点: + - 支持任意类型的配置文件 + - 对于.properties配置文件,配置更新时,自动注入reload + - 无代码侵入 + - 适合于旧项目的迁移 +- 缺点: + - 需要在xml定义 java bean + - 非.properties配置更新时无法自动注入java bean里,你可以写回调函数来支持自动注入。 + +在这里将以 disconf-standalone-demo中的 demo为例,讲解如何实现 无代码侵入的分布式配置 + +## 第一步:上传配置文件 + +上传 autoconfig.properties 至 disconf-web里 + +## 第二步:修改配置文件 + +添加 基本的 disconf支持 + + + + + + + + + + + + +注:从版本`2.6.30`开始,不再需要扫描包`com.baidu`了,扫描自己的包即可。即: + + + + + + + + + + + + +特别的,添加 需要进行托管的 配置文件: + + + + + + classpath:/autoconfig.properties + classpath:/autoconfig2.properties + + + + + + + + + + + + + + +在这里,添加了 6个配置文件,其中有4个.properties, 2个非properties文件 + +## 添加需要配置的java bean + + + + + + + + + + +java bean 与 传统的 spring 写法没有任何区别 + +## 补充 + +如果想配置文件,但是不想自动reload,那么该怎么办? + +可以使用以下与本方法非常相似的做法: + + + + + + myserver.properties + + + + + + + + + + + + + + +在这里,myserver.properties被disconf托管,当在disconf-web上修改配置文件时,配置文件会被自动下载至本地,但是不会reload到系统里。 + +具体可参见:[Tutorial5](Tutorial5.html) + +## 完结 + +当在disconf-web中对 4个properties文件中的任何一个文件更新时,所有 使用这些配置文件的java bean都将自动重新注入。无需重启程序。 + +非properties文件,则需要重启程序才可以支持。当然你可以写回调函数来支持自动注入。 + + + diff --git a/docs/source/tutorial-client/Tutorial9.md b/docs/source/tutorial-client/Tutorial9.md new file mode 100644 index 000000000..90befdf6f --- /dev/null +++ b/docs/source/tutorial-client/Tutorial9.md @@ -0,0 +1,35 @@ +Tutorial 9 实现真正意义上的统一上线包 +======= + +### 问题 + +一直以来,凡是使用 disconf的程序均需要 `disconf.properties` ,在这个文件里去控制 app/env/version。 + +因此,我们要部署到不同的环境中,还是需要 不同的 `disconf.properties`。 + +有一种解决方法是,通过 jenkins 来进行打包,准备多份 `disconf.properties` 文件。 + +### 解决方法 + +真正的解决方法是,使用 java 命令行参数 + +目前 disconf 已经支持 `disconf.properties` 中所有配置项 通过参数传入方式 启动。 + +支持的配置项具体可参见: [link](../../config/client-config.html) + +这样的话,未来大家只要通过 Java 参数 就可以 动态的改变启动的 app/env/version + +#### standalone 启动示例 + + java -Ddisconf.env=rd \ + -Ddisconf.enable.remote.conf=true \ + -Ddisconf.conf_server_host=127.0.0.1:8000 \ + -Dlogback.configurationFile=logback.xml \ + -Dlog4j.configuration=file:log4j.properties \ + -Djava.ext.dirs=lib \ + -Xms1g -Xmx2g -cp ampq-logback-client-0.0.1-SNAPSHOT.jar \ + com.github.knightliao.consumer.ConsumerMain >/dev/null 2>&1 & + +这里表示使用 disconf.env=rd + +#### tomcat 启动示例 \ No newline at end of file diff --git a/docs/source/tutorial-client/config-getter.md b/docs/source/tutorial-client/config-getter.md new file mode 100644 index 000000000..314c84ee1 --- /dev/null +++ b/docs/source/tutorial-client/config-getter.md @@ -0,0 +1,71 @@ +统一类获取任何配置数据 +============== + +## 2.6.34 版本起开始支持 ## + +### 主要升级点 + +增加统一的类 来个性化编程式的获取任何配置数据, 目前只支持 .properties 文件 + +### 接口 + + public class DisconfDataGetter { + + private static IDisconfDataGetter iDisconfDataGetter = new DisconfDataGetterDefaultImpl(); + + /** + * 根据 分布式配置文件 获取该配置文件的所有数据,以 map形式呈现 + * + * @param fileName + * + * @return + */ + public static Map getByFile(String fileName) { + return iDisconfDataGetter.getByFile(fileName); + } + + /** + * 获取 分布式配置文件 获取该配置文件 中 某个配置项 的值 + * + * @param fileName + * @param fileItem + * + * @return + */ + public static Object getByFileItem(String fileName, String fileItem) { + return iDisconfDataGetter.getByFileItem(fileName, fileItem); + } + + /** + * 根据 分布式配置 获取其值 + * + * @param itemName + * + * @return + */ + public static Object getByItem(String itemName) { + return iDisconfDataGetter.getByItem(itemName); + } + } + +### 使用方式 + +获取 配置文件 redis.properties 的KV值: + + DisconfDataGetter.getByFile("redis.properties"); + +获取 配置文件 autoconfig.properties 的KV值: + + DisconfDataGetter.getByFile("autoconfig.properties") + +获取 配置文件 autoconfig.properties 中 key为 auto 的值: + + DisconfDataGetter.getByFile("autoconfig.properties").get("auto") + +获取 配置文件 autoconfig.properties 中 key为 auto 的值: + + DisconfDataGetter.getByFileItem("autoconfig.properties", "auto") + +获取 配置项 moneyInvest 的值: + + DisconfDataGetter.getByItem("moneyInvest"); \ No newline at end of file diff --git a/docs/source/tutorial-client/index.rst b/docs/source/tutorial-client/index.rst new file mode 100644 index 000000000..c7a027487 --- /dev/null +++ b/docs/source/tutorial-client/index.rst @@ -0,0 +1,21 @@ +Tutorial-client +=============== + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/Tutorial1 + src/Tutorial2 + src/Tutorial3 + src/Tutorial4 + src/Tutorial5 + src/Tutorial7 + src/Tutorial8 + src/Tutorial9 + src/Tutorial10 + src/Tutorial11-config-download-path + src/Tutorial13-unify-notify + src/Tutorial14-bean-setter-mode + src/jar-start-up + src/config-getter diff --git a/docs/source/tutorial-client/jar-start-up.md b/docs/source/tutorial-client/jar-start-up.md new file mode 100644 index 000000000..8c4367527 --- /dev/null +++ b/docs/source/tutorial-client/jar-start-up.md @@ -0,0 +1,77 @@ +-jar jar包启动支持 +============ + +## 2.6.33 版本起开始支持 ## + +### 主要升级点 + +当使用以 -jar 方式启动的程序(非tomcat,web方式)时,例如 springboot 时,可以无缝对接(不会出现配置文件找不到的情况) + +### 正确的使用方式 + + + + + + classpath*:autoconfig.properties + + + + + + + + + + + + + + +### 细节 + +在未升级前,要使用spring-boot,我们的配置可能是这样的 + + + + + + file:autoconfig.properties + + + + + + + + + + + + + + +注意,我们使用 `file:autoconfig.properties` 而不是 `classpath*:autoconfig.properties`。这是为什么呢? + +当以 -jar 的方式启动服务时,classpath可能会找不到,spring读取配置的原则是: + +- `classpath*:autoconfig.properties` : 只能读取jar包内的配置文件 +- `file:autoconfig.properties` : 读取的当前路径下的配置文件 + +当以 java入口类 的方式启动服务时,classpath一定是WEB-INF/classes或者target/classes目录下,spring读取配置的原则是: + +- `classpath*:autoconfig.properties` : 读取的WEB-INF/classes或者target/classes目录下的配置文件 +- `file:autoconfig.properties` : 读取的是当前路径下的配置文件 + +在以-jar方式启动时,原则上,我们希望将配置文件放在jar包外面,所以这里采用了 `file:autoconfig.properties`,当以这种方式启动程序时,disconf将配置下载到当前路径下,程序可以找到配置,不会报错。 + +但当你使用IDE(eclipse or intellij)启动时,它就报错了,说配置文件找不到了。这是因为IDE启动时,不是以-jar的方式启动的,它是以Java入口类的方式进行启动的,disconf将配置下载到target/classes +目录下,如果还是使用`file:autoconfig.properties`,程序在当前路径下是无法找到的。 + +这就产生了一个问题:我在IDE调试时,必须使用 `classpath*:autoconfig.properties` ,当以命令行启动时,必须使用 `file:autoconfig.properties` + +本次升级就是为了避免这个问题。未来配置统一是`classpath*:autoconfig.properties`。原理是,在以-jar启动时,通过将当前路径设置为classpath,程序启动后,disconf将配置下载到当前路径,通过读取classpath进行读取配置,就可以找到这个配置了。 \ No newline at end of file diff --git a/docs/source/tutorial-client/src/Tutorial1.rst b/docs/source/tutorial-client/src/Tutorial1.rst new file mode 100644 index 000000000..225507b7e --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial1.rst @@ -0,0 +1,521 @@ +Tutorial 1 注解式分布式的配置文件(最佳实践) +============================================= + +这里以 disconf-demo/disconf-standalone-demo +某个程序片段为例,详细介绍了一个 分布式配置文件 的简单示例程序。 + +假设,我们的应用程序使用了Redis服务,我们将使用Jedis来进行编程。编程时,我们需要Redis的Host和Port,通常情况下,我们会把这两个参数放在配置文件里。 + +**本教程将以两个部分来进行**\ , + +- 第一部分讲解正常情况下(不使用Disconf)的写法,这是我们以前常做的事情 + 。 +- 第二部分,会在第一部分的基础上,添加Disconf的支持。从这一部分,大家就可以看到Disconf的简洁性和低侵入性。 + 并且,大家也可以看到关闭和开启Disconf,原有程序(第一部分)都可以正确Work。 + +第一部分:一个简单普通的Redis程序 +--------------------------------- + +第一步:准备一个配置文件 redis.properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +我们需要一个 redis.properties 文件,里面含有 Host 和 Port。文件内容是: + +:: + + redis.host=127.0.0.1 + redis.port=6379 + +我们需要把此文件放在项目的ClassPath路径下。 + +第二步:撰写配置文件相应的配置文件类 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +我们撰写一个类JedisConfig,它与 redis.properties +相对应。整个类的完整代码如下: + +:: + + package com.example.disconf.demo.config; + + import org.springframework.stereotype.Service; + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + public class JedisConfig { + + // 代表连接地址 + private String host; + + // 代表连接port + private int port; + + /** + * 地址 + * + * @return + */ + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + /** + * 端口 + * + * @return + */ + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + } + +注意,这里的Get&Set方法均是Eclipse自动生成的。 + +在applicationContext.xml 添加以下代码,目的是将配置值注入到此类中: + +:: + + + + + + + + + classpath*:/redis.properties + + + + + + + + + +第三步:一个简单的Redis服务程序 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +我们的初衷是使用Redis服务。因此,我们需要撰写一个连接Redis的Service类,它使用的是第二步里的配置文件类。完整类的实现代码如下: + +:: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.DisposableBean; + import org.springframework.beans.factory.InitializingBean; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Service; + + import redis.clients.jedis.Jedis; + + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.utils.JedisUtil; + + /** + * 一个简单的Redis服务 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + public class SimpleRedisService implements InitializingBean, DisposableBean { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(SimpleRedisService.class); + + // jedis 实例 + private Jedis jedis = null; + + /** + * 分布式配置 + */ + @Autowired + private JedisConfig jedisConfig; + + /** + * 关闭 + */ + public void destroy() throws Exception { + + if (jedis != null) { + jedis.disconnect(); + } + } + + /** + * 进行连接 + */ + public void afterPropertiesSet() throws Exception { + + jedis = JedisUtil.createJedis(jedisConfig.getHost(), + jedisConfig.getPort()); + } + + /** + * 获取一个值 + * + * @param key + * @return + */ + public String getKey(String key) { + if (jedis != null) { + return jedis.get(key); + } + + return null; + } + } + +**具体步骤是:** + +#. 此类实现了 InitializingBean, DisposableBean + 两个接口,目的是在Bean初始化后进行Redis的连接。 +#. 为此类添加 @Service ,代表它是一个Bean。Spring托管的,且 "scope" + 都必须是singleton的。 + +第四步:使用SimpleRedisService +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +使用起来非常简单, 示例如下: + +:: + + package com.example.disconf.demo.task; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Service; + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.service.SimpleRedisService; + + /** + * 演示分布式配置文件、分布式配置的更新Demo + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + public class DisconfDemoTask { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(DisconfDemoTask.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + @Autowired + private JedisConfig jedisConfig; + + private static final String REDIS_KEY = "disconf_key"; + + /** + * + */ + public int run() { + + try { + + while (true) { + + Thread.sleep(5000); + + LOGGER.info("redis( " + jedisConfig.getHost() + "," + + jedisConfig.getPort() + ") get key: " + REDIS_KEY + + } + + } catch (Exception e) { + + LOGGER.error(e.toString(), e); + } + + return 0; + } + } + +第二部分:支持分布式配置(disconf)的简单Redis程序 +-------------------------------------------------- + +第一步:添加Disconf的支持 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +在applicationContext.xml里添加Bean定义: + +:: + + + + + + + + +| 其中这里,我们定义 属性“scanPackage” 的值是 com.example.disconf.demo。 +| 这里需要填上你的项目的Package名。这与Spring的auto scan包名功能一样。 + +另外,从2.6.23起,这里的 ``scanPackage`` +属性支持扫描多包,逗号分隔,例如: + +:: + + + + + +第二步 项目准备 +~~~~~~~~~~~~~~~ + +修改扫描类 +^^^^^^^^^^ + +你的项目的扫描类是com.example,为了支持disconf,因此,必须添加扫描类 +com.baidu ,如: + +:: + + + +注:从版本\ ``2.6.30``\ 开始,不再需要扫描包\ ``com.baidu``\ 了,扫描自己的包即可。即: + +:: + + + +支持 cglib aop +^^^^^^^^^^^^^^ + +使你的项目支持 cglib的aop + +:: + + + +第三步:修改JedisConfig支持分布式配置 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +我们撰写一个类JedisConfig,它与 redis.properties +相对应。整个类的完整代码如下: + +:: + + package com.example.disconf.demo.config; + + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @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; + } + } + +**具体步骤是:** + +#. 为这个类 JedisConfig 定义 @DisconfFile 注解,必须指定文件名为 + redis.properties 。 +#. 定义域 host port,分别表示Host和Port。并使用Eclipse为其自动生成 + get&set 方法。 +#. 为这两个域的get方法上添加注解 @DisconfFileItem 。添加标记 name, + 表示配置文件中的KEY名,这是必填的。标记associateField是可选的,它表示此get方法相关连的域的名字,如果此标记未填,则系统会自动分析get方法,猜测其相对应于域名。强烈建议添加associateField标记,这样就可以避免Eclipse生成的Get/Set方法不符合Java规范的问题。 +#. 标记它为Spring托管的类 + (\ `使用@Service `__\ ),且 "scope" + 都必须是singleton的。 + +**注意:** + +Eclipse自动生成的get方法,可能与Java的规范不同。这会导致很多问题。因此,建议加上 +associateField 标记。 + +第四步:添加 disconf.properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**准备disconf.properties文件:** + +Disconf启动需要此文件,文件示例是: + +:: + + # 是否使用远程配置文件 + # true(默认)会从远程获取配置 false则直接获取本地配置 + enable.remote.conf=true + + # + # 配置服务器的 HOST,用逗号分隔 127.0.0.1:8000,127.0.0.1:8000 + # + conf_server_host=127.0.0.1:8080 + + # 版本, 请采用 X_X_X_X 格式 + version=1_0_0_0 + + # APP 请采用 产品线_服务名 格式 + app=disconf_demo + + # 环境 + env=rd + + # debug + debug=true + + # 忽略哪些分布式配置,用逗号分隔 + ignore= + + # 获取远程配置 重试次数,默认是3次 + conf_server_url_retry_times=1 + # 获取远程配置 重试时休眠时间,默认是5秒 + conf_server_url_retry_sleep_seconds=1 + +配置相关说明可参考:\ `配置 <../config/client-config.html>`__ + +注意:如果使用Disconf,则本地的配置文件\ ``redis.properties``\ 可以删除掉(也可以不删除掉,建议删除掉)。如果不使用Disconf,则需要此配置文件。 + +第五步:在\ ``disconf-web``\ 上上传配置文件(\ ``redis.properties``\ ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +当你的程序启动时,disconf就会帮忙你的程序去获取配置文件。那如何让disconf知道你的配置呢?答案是需要在disconf-web上传配置文件哦。 + +点击主页面的新建配置文件按钮: + +|image0| + +进入页面后就可以上传 配置文件了 + +|image1| + +第六步:在\ ``disconf-web``\ 上查看 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +你在第五步上传了配置文件 redis.properties ,那么 +,当你的程序启动时,disconf就会帮忙你的程序去获取配置文件。 + +可以看到已经有一个实例在使用redis.properties了。 + +|image2| + +点击查看它的详情,可以看到,确实是我的实例在使用它。 + +|image3| + +完结 +~~~~ + +至此,分布式配置文件的撰写就已经写完了。 + +可以看到,基于注解的方式,不需要在xml定义 java bean(config类). + +使用方便 +^^^^^^^^ + +大家可以看到,第一次使用时,需要 + +- 在\ ``applicationContext.xml``\ 添加Disconf启动支持 +- 使用注解方式 修改配置类 +- 添加\ ``disconf.properties`` +- 在\ ``disconf-web``\ 上上传配置文件 + +非第一次使用时,需要 + +- 使用注解方式 修改配置类 +- 在\ ``disconf-web``\ 上上传配置文件 + +就可以支持分布式配置了。 + +强兼容性 +^^^^^^^^ + +并且,如果将 ``disconf.disconf.properties`` 中的 ``enable.remote.conf`` +设置为 ``false`` + +| 那么,分布式配置就会失效,退化为 + 使用本地配置方式(即第一部分的功能)。(如果是这种情况,你必须确认你本地留有相应的配置文件。 +| 一般来说,只要成功跑过一次基于disconf的程序,那么classpath目录下就会有此程序的所有相应配置文件。) + +并且,如果 +disconf-web无法正常服务(\ ``conf_server_host=127.0.0.1:8080``\ ),分布式配置也会失效,退化为 +使用本地配置方式(即第一部分的功能)。(如果是这种情况,你必须确认你本地留有相应的配置文件) + +也就是说,Disconf是具有兼容性的 + +- 当开启Disconf时, + + - 如果Disconf正常运行,则正常使用分布式配置。 + - 如果Disconf非正常运行,则使用本地配置。(Disconf可以保证在Disconf失败时,原有程序能够按原有逻辑正确运行) + +- 当不开启Disconf时, 则使用本地配置。 + +**注:** + +#. 只要是运行一次分布式程序成功,则本地就含有最全的配置文件。此时,如果再运行一次分布式程序,如果出现失败,则上一次下载成功的配置文件就会当成本地配置生效,程序成功启动。 + +END +--- + +.. |image0| image:: http://ww3.sinaimg.cn/mw1024/60c9620fjw1em9mgdgkn8j20ti04x3za.jpg +.. |image1| image:: http://ww1.sinaimg.cn/mw1024/60c9620fjw1em9mkivysij20q20fk0tm.jpg +.. |image2| image:: http://ww4.sinaimg.cn/mw1024/60c9620fgw1ekdf6ymx9yj20rc0ey0w3.jpg +.. |image3| image:: http://ww1.sinaimg.cn/mw1024/60c9620fgw1ekdf8i229bj20rt0fotbt.jpg + diff --git a/docs/source/tutorial-client/src/Tutorial10.rst b/docs/source/tutorial-client/src/Tutorial10.rst new file mode 100644 index 000000000..c72df9919 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial10.rst @@ -0,0 +1,20 @@ +Tutorial 10 实现一个配置更新下载器agent +======================================= + +问题 +~~~~ + +我想在我的机器上做一个配置下载器agent, 可以实现以下功能: + +- 启动时下载配置 +- 配置被更新时,可以感知并下载下来 + +解决方法 +~~~~~~~~ + +| 可以修改一下 disconf-demos/disconf-standalone-demo + 这个项目,让其变成一个 长驻进程,并指定 +| `disconf.user\_define\_download\_dir <../../config/client-config.html>`__ + 这个配置到你想指定的路径。 + +done. diff --git a/docs/source/tutorial-client/src/Tutorial11-config-download-path.rst b/docs/source/tutorial-client/src/Tutorial11-config-download-path.rst new file mode 100644 index 000000000..5e9e7b540 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial11-config-download-path.rst @@ -0,0 +1,56 @@ +Tutorial 11 配置文件下载地址讨论 +================================ + +问题一 从disconf下载的配置文件都放到哪里去了? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +解决:按以下顺序进行判断 +~~~~~~~~~~~~~~~~~~~~~~~~ + +对于注解式配置文件: + +- 一定会下载到 disconf.user\_define\_download\_dir + 目录下(使用此方法可以方便的构造一个下载器. + `Tutorial10 `__ ) +- 如果 disconf.enable\_local\_download\_dir\_in\_class\_path + 为true(默认值), 则会执行以下判断: + + - 如果 @DisconfFile 的 targetDirPath 值不为空, 则会下载到 + targetDirPath 这个目录下, + 配置数据从该路径读取。这对于那些不想放在classpath根目录的程序, + 比较有用. + - 如果 @DisconfFile 的 targetDirPath 值为空, + 则会下载到classpath路径下, 配置数据从该路径读取. + +- 如果 disconf.enable\_local\_download\_dir\_in\_class\_path 为false, + 则不会下载到classpath目录下. 配置数据从 + disconf.user\_define\_download\_dir 读取 + +对于XML式配置文件: + +- 一定会下载到 disconf.user\_define\_download\_dir + 目录下(使用此方法可以方便的构造一个下载器). +- 如果 disconf.enable\_local\_download\_dir\_in\_class\_path + 为true(默认值), 则会执行以下判断: + + - 如果 @DisconfFile 的 targetDirPath 值不为空, 则会下载到 + targetDirPath 这个目录下. + - 如果 @DisconfFile 的 targetDirPath 值为空, + 则会下载到classpath路径下. + +- 如果 disconf.enable\_local\_download\_dir\_in\_class\_path 为false, + 则不会下载到classpath目录下. + +注: + +#. 如果不作任何配置的改变,默认情况下,会下载到 + disconf.user\_define\_download\_dir 目录 和 classpath 两个目录下。 +#. targetDirPath + 值说明:以"/"开头则是系统的全路径,否则则是相对于classpath的路径,默认是classpath根路径。注意:根路径要注意是否有权限,否则会出现找不到路径,推荐采用相对路径。 +#. 配置说明看这里 `config <../../config/client-config.html>`__ + +问题二 不想下载到classpath目录下 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +将 disconf.enable\_local\_download\_dir\_in\_class\_path 为false, 并 +指定 下载目录 disconf.user\_define\_download\_dir diff --git a/docs/source/tutorial-client/src/Tutorial13-unify-notify.rst b/docs/source/tutorial-client/src/Tutorial13-unify-notify.rst new file mode 100644 index 000000000..cbbf8fd3b --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial13-unify-notify.rst @@ -0,0 +1,44 @@ +Tutorial 13 增加统一的回调类 (unify-notify模式) 灵活处理更新配置通知 +==================================================================== + +目的 +~~~~ + +当 任意的 配置文件 或 配置项 得到更新时,此类 就会被调用。 + +它与 `Tutorial2 `__ +不一样,不需要注解,不需要必须指定变更地象。更加freely,方便大家在这里统一的、自由的控制更新逻辑. + +示例项目 +~~~~~~~~ + +https://github.com/knightliao/disconf-demos-java/tree/master/disconf-standalone-demo + +demo +~~~~ + +只要实现 ``IDisconfUpdatePipeline`` 接口即可。不要求必须是 java bean. + +- 函数 ``reloadDisconfFile`` + 是针对分布式配置文件的。key是文件名;filePath是文件路径。用户可以在这里(read + file freely)按你喜欢的解析文件的方式进行处理。 +- 函数 ``reloadDisconfItem`` + 是针对分布式配置项的。key是配置项名;content是其值,并且含有类型信息。 + +示例代码: + +:: + + /** + */ + @Service + public class UpdatePipelineCallback implements IDisconfUpdatePipeline { + + public void reloadDisconfFile(String key, String filePath) throws Exception { + System.out.println(key + " : " + filePath); + } + + public void reloadDisconfItem(String key, Object content) throws Exception { + System.out.println(key + " : " + content); + } + } diff --git a/docs/source/tutorial-client/src/Tutorial14-bean-setter-mode.rst b/docs/source/tutorial-client/src/Tutorial14-bean-setter-mode.rst new file mode 100644 index 000000000..c6debccc4 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial14-bean-setter-mode.rst @@ -0,0 +1,223 @@ +Tutorial 14 配置初始化 或 更新时,通知采用 "bean setter模式" +============================================================ + +目的 +~~~~ + +当通过 java bean 进行控制配置时,当某个配置有初始化或更新时,可以在 bean +property setter 方法里 做适合自己的业务逻辑。 + +示例项目 +~~~~~~~~ + +https://github.com/knightliao/disconf-demos-java/tree/master/disconf-standalone-demo + +demo1: java 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; + LOGGER.info("i' m here: setting redis port"); + } + + public void reload() throws Exception { + LOGGER.info("host: " + host); + } + } + +在这里的 setPort 方法 会在该 javabean ``初始化`` 或者 ``配置更新`` 时 +被调用 。 + +:: + + public void setPort(int port) { + this.port = port; + LOGGER.info("i' m here: setting redis port"); + } + +demo2: static class 注解式配置 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + /** + */ + @DisconfFile(filename = "static.properties") + public class StaticConfig { + + protected static final Logger LOGGER = LoggerFactory.getLogger(StaticConfig.class); + + private static int staticVar; + + @DisconfFileItem(name = "staticVar", associateField = "staticVar") + public static int getStaticVar() { + return staticVar; + } + + public static void setStaticVar(int staticVar) { + StaticConfig.staticVar = staticVar; + LOGGER.info("i' m here: setting static class variable"); + } + + } + +在这里的 setStaticVar 方法 会在该 class ``初始化`` 或者 ``配置更新`` 时 +被调用 。 + +demo3: 基于XML配置文件的无侵入式 配置 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class: + +:: + + /** + */ + public class AutoService { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AutoService.class); + + private String auto; + + public String getAuto() { + return auto; + } + + public void setAuto(String auto) { + this.auto = auto; + LOGGER.info("i' m here: setting auto"); + } + } + +配置: + +:: + + + + + + classpath:/autoconfig.properties + + + + + + + + + + + + + + +demo4: 配置项 配置 +~~~~~~~~~~~~~~~~~~ + +:: + + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory.getLogger(BaoBaoService.class); + + public static final String key = "moneyInvest"; + + @Value(value = "2000d") + private Double moneyInvest; + + @Autowired + private Coefficients coefficients; + + /** + * 计算百发一天赚多少钱 + * + * @return + */ + public double calcBaiFa() { + return coefficients.getBaiFaCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * k 计算余额宝一天赚多少钱 + * + * @return + */ + public double calcYuErBao() { + return coefficients.getYuErBaoCoe() * coefficients.getDiscount() * getMoneyInvest(); + } + + /** + * 投资的钱,分布式配置
+ *
+ * 这里切面无法生效,因为SpringAOP不支持。
+ * 但是这里还是正确的,因为我们会将值注入到Bean的值里. + * + * @return + */ + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + + public void setMoneyInvest(Double moneyInvest) { + this.moneyInvest = moneyInvest; + LOGGER.info("i' m here: setting moneyInvest"); + } + + } + +在这里的 setMoneyInvest 方法 会在该 class ``初始化`` 或者 ``配置更新`` +时 被调用 。 + +配置更新时通知的所有方式 总结 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 指定key的注解式 `Tutorial2 `__ +- 统一通知模式 unify-notify `Tutorial13 `__ +- bean setter模式 diff --git a/docs/source/tutorial-client/src/Tutorial2.rst b/docs/source/tutorial-client/src/Tutorial2.rst new file mode 100644 index 000000000..269a62ede --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial2.rst @@ -0,0 +1,263 @@ +Tutorial 2 注解式分布式的配置文件高级篇: 配置更新的通知(最佳实践) +=================================================================== + +| 在 `Tutorial 1 `__ 里, +| 我们实现了一个简单的Redis服务程序,它使用分布式配置进行管理,此Redis的配置文件存储在分布式服务器 + disconf-web 上。 + +也许有一天,我们需要更新Redis的Host和Port配置数据。由于 +redis是根据配置生成的实例,因此,这种情况下,你有三种选择: + +- 不使用Disconf(\ `Tutorial 1 `__ + 里第一部分的使用方法)。那么你需要 更改线上机器的配置文件 + redis.properties,重启服务就可以了。 +- 使用Disconf, 采用 `Tutorial 1 `__ + 里第二部分的使用的方案。那么你需要 更改 分布式服务器 disconf-web 上的 + redis.properties 文件。 + 然后重启服务就可以了。和第一种方法的区别在于,不需要更改线上机器的配置文件。 +- 使用Disconf,采用 `Tutorial 1 `__ + 里第二部分的使用的方案,并额外加上本Tutorial的方案,那么你 只需要 + 更改 分布式服务器 disconf-web 上的 redis + .properties 文件。然后服务的配置自动生效。此过程无需要重新启动服务。 + +本教程就是阐述如何通过简单的配置和极简代码实现第三步的功能。 + +第一步:准备工作 +---------------- + +完成 `Tutorial 1 `__ 上第二部分方案里的步骤。 + +第二步:修改 SimpleRedisService,支持Redis重连接 +------------------------------------------------ + +在这里,我们这个类添加了一个方法,重新生成了一个Jedis对象,代码如下: + +:: + + /** + * 更改Jedis + */ + 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."); + } + +之所以添加这个函数的原因是:在配置更新时,此函数要被调用,从而更改Jedis实例。 + +整个类的完整代码如下: + +:: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.DisposableBean; + import org.springframework.beans.factory.InitializingBean; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.utils.JedisUtil; + + import redis.clients.jedis.Jedis; + + /** + * 一个简单的Redis服务 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + public class SimpleRedisService implements InitializingBean, DisposableBean { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisService.class); + + // jedis 实例 + private Jedis jedis = null; + + /** + * 分布式配置 + */ + @Autowired + private JedisConfig jedisConfig; + + /** + * 关闭 + */ + public void destroy() throws Exception { + + if (jedis != null) { + jedis.disconnect(); + } + } + + /** + * 进行连接 + */ + public void afterPropertiesSet() throws Exception { + + jedis = JedisUtil.createJedis(jedisConfig.getHost(), jedisConfig.getPort()); + } + + /** + * 获取一个值 + * + * @param key + * + * @return + */ + public String getKey(String key) { + if (jedis != null) { + return jedis.get(key); + } + + return null; + } + + /** + * 更改Jedis + */ + 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."); + } + } + +第三步: 撰写配置更新回调类 +--------------------------- + +当配置更新时,应用程序要得到通知。因此我们要写一个回调类来响应此“通知”。完整的类如下: + +:: + + package com.example.disconf.demo.service.callbacks; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.context.annotation.Scope; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfUpdateService; + import com.baidu.disconf.client.common.update.IDisconfUpdate; + import com.example.disconf.demo.config.Coefficients; + import com.example.disconf.demo.config.JedisConfig; + import com.example.disconf.demo.service.SimpleRedisService; + + /** + * 更新Redis配置时的回调函数 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @Scope("singleton") + @DisconfUpdateService(classes = {JedisConfig.class}, itemKeys = {Coefficients.key}) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisServiceUpdateCallback.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + /** + * + */ + public void reload() throws Exception { + + simpleRedisService.changeJedis(); + } + + } + +**具体步骤是:** + +- 撰写此类,实现 IDisconfUpdate + 接口。此类必须是JavaBean,Spring托管的,且 "scope" + 都必须是singleton的。 +- 添加 @DisconfUpdateService 注解,classes 值加上 JedisConfig.class + ,表示当 JedisConfig.class + 这个配置文件更新时,此回调类将会被调用。或者,使用 + confFileKeys 也可以。 +- 在函数 reload() 里调用 SimpleRedisService 的 changeJedis() 方法 + +回调类与配置类放在一起 +~~~~~~~~~~~~~~~~~~~~~~ + +如果你觉得写两个类太累,在某些场景下,则可以将回调与配置类放在一起的。 + +:: + + /** + * Redis配置文件 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @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 { + LOGGER.info("host: " + host); + } + } + +完结 +---- + +至此,支持配置更新的 分布式配置文件 的撰写就已经写完了。 + +当用户在 disconf-web 上更新配置时,你的服务里的Redis就会指向新的地址。 diff --git a/docs/source/tutorial-client/src/Tutorial3.rst b/docs/source/tutorial-client/src/Tutorial3.rst new file mode 100644 index 000000000..9e3b011a6 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial3.rst @@ -0,0 +1,307 @@ +Tutorial 3 注解式分布式的配置项(最佳实践) +=========================================== + +| 在 `Tutorial 1 `__ 里, +| 我们实现了一个简单的Redis服务程序,它使用注解式的分布式配置进行管理,此Redis的配置文件存储在分布式服务器 + disconf-web 上。 + +那如果你的配置只是一个变量,不是配置文件,怎么办?还能实现分布式配置吗? + +答案当然是肯定的! + +Disconf支持配置项(配置项是指 一个类的某个域变量)的分布式化。 + +这里以 disconf-demo 某个程序片段为例,详细介绍了 分布式的配置项 +的简单示例程序。 + +在这里,我们将分两种情况来进行演示: + +#. 配置项在某个配置类里:外部程序通过配置类的get\ ``***``\ 方法获取。 +#. 配置项在某个Service类里。Service通过本身类的的get\ ``***``\ 方法获取。 + +对于这两种方式,Disconf的后台实现方式上有所不同。这里不会讲述具体原因。但是Disconf做到了兼容性,以上两种方式均支持。 + +配置类里的配置项 +---------------- + +第一步:撰写 配置项类 +~~~~~~~~~~~~~~~~~~~~~ + +这里假设有一个金融系数类,它有一个折扣率变量。 + +:: + + package com.example.disconf.demo.config; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + import com.baidu.disconf.client.common.annotations.DisconfItem; + + + /** + * 金融系数文件 + * + **/ + @Service + @DisconfFile(filename = "coefficients.properties") + 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; + } + } + +**具体步骤是:** + +#. 编写Coefficients类,添加域 discount +#. 用Eclipse为域discount添加 get & set方法 +#. 为get方法添加注解 @DisconfItem(key = key) + ,这里的key是分布式配置项的标识,这里是 discountRate +#. 此类必须是JavaBean,Spring托管的,且 "scope" 都必须是singleton的。 +#. `可以使用@Value为它设置一个默认值 `__\ 。 + +第二步:使用此分布式配置项 +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +撰写一个Service类,它使用 第二步里的 discountRate 变量,完整的类是: + +:: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.Coefficients; + + /** + * 金融宝服务,计算一天赚多少钱 + * + * @author liaoqiqi + * @version 2014-5-16 + */ + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(BaoBaoService.class); + + @Autowired + private Coefficients coefficients; + + /** + * + * + * @return + */ + public double calcMoney() { + return 10000 * coefficients.getDiscount(); + } + + } + +calcMoney()会调用 coefficients.getDiscount() 获取折扣率 来计算 +真正的money. + +第三步:配置项更新回调类 +~~~~~~~~~~~~~~~~~~~~~~~~ + +当配置项更新时,你的服务程序自动就会获取最新的配置项数据(不需要写回调函数,因为这里不像Redis这种较“重”的服务,这里的配置是实时生效的)。 + +但是,如果当你的配置项更新时,配置项本身被更新后,可能还会其它类依赖此配置项的更新,那么,你需要撰写一个回调类来获取此通知。 + +为了简单,这里我们以 `Tutorial 2 `__ 里的 +SimpleRedisServiceUpdateCallback 类 为基础,进行扩展。 + +假设,当此配置项被更新时,Redis也需要重新被reload,那么,你可以这样来改写 +SimpleRedisServiceUpdateCallback 类,完整的代码如下: + +:: + + package com.example.disconf.demo.service.callbacks; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfUpdateService; + import com.baidu.disconf.client.common.update.IDisconfUpdate; + import com.example.disconf.demo.config.Coefficients; + import com.example.disconf.demo.config.JedisConfig; + + /** + * 更新Redis配置时的回调函数 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @Service + @DisconfUpdateService(classes = { JedisConfig.class }, itemKeys = { Coefficients.key }) + public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(SimpleRedisServiceUpdateCallback.class); + + @Autowired + private SimpleRedisService simpleRedisService; + + /** + * + */ + public void reload() throws Exception { + + simpleRedisService.changeJedis(); + } + + } + +这里通过为注解 @DisconfUpdateService 添加一个 itemKeys: Coefficients.key +,就实现了配置项更新的通知。怎么样?是不是很强大? + +第四步:在\ ``disconf-web``\ 上上传配置 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +上传方式是先在首页点击 新建配置项 + +|image0| + +然后新建就行啦 + +|image1| + +完结 +~~~~ + +通过几行简单的配置,分布式配置项 就这样添加到你的应用程序里了。 + +Service类的配置项 +----------------- + +在上一节里,我们阐述了如何在 配置项类 里添加一个配置项的方法。 + +在本节里,我们将在上一部分的基础上,阐述如何实现 不创建配置项类 就可以 +实现 分布式配置项 的方法。 + +准备 +~~~~ + +在 分布式配置服务器 disconf-web 上添加 moneyInvest 和 discountRate +配置项值。 + +第一步:撰写 含有 配置项 的Service类 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +在上一节里,我们撰写了一个 Coefficients.java 类,它含有 分布式配置项 +discountRate,BaoBaoService.java 则是一个使用 discountRate +的服务。BaoBaoService.java 在计算(calcMoney)时,使用了固定值 10000. + +在本节里,我们 将 10000 这个值动态化,标注为分布式配置项。 + +完整的类是: + +:: + + package com.example.disconf.demo.service; + + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.Coefficients; + + /** + * + * @author liaoqiqi + * @version 2014-5-16 + */ + @Service + public class BaoBaoService { + + protected static final Logger LOGGER = LoggerFactory + .getLogger(BaoBaoService.class); + + public static final String key = "moneyInvest"; + + private Double moneyInvest = 1000d; + + @Autowired + private Coefficients coefficients; + + /** + * + * @return + */ + public double calcMoney() { + return coefficients.getDiscount() + * getMoneyInvest(); + } + + /** + * 投资的钱,分布式配置
+ *
+ * 这里切面无法生效,因为SpringAOP不支持。
+ * 但是这里还是正确的,因为我们会将值注入到Bean的值里. + * + * @return + */ + @DisconfItem(key = key) + public Double getMoneyInvest() { + return moneyInvest; + } + + public void setMoneyInvest(Double moneyInvest) { + this.moneyInvest = moneyInvest; + } + + } + +**具体实现步骤是:** + +#. 添加域 moneyInvest ,并使用Eclipse自动生成 get&set 方法。 +#. 为get方法添加 @DisconfItem 注解,并添加 key 为 moneyInvest +#. 在函数 calcMoney() 里 调用本身类的 getMoneyInvest() 方法。 +#. 此类必须是JavaBean,Spring托管的,且 "scope" 都必须是singleton的。 + +第二步:在\ ``disconf-web``\ 上上传配置 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +这里不再赘述。 + +完结 +~~~~ + +只需要上面一步,就完成了分布式配置项。 + +配置更新也是实时的,不需要写回调函数。 + +service类的配置项 其实和 配置项类的配置项 撰写方法 +差不多。它的好处是不需要再新建一个配置项类。 + +.. |image0| image:: http://ww3.sinaimg.cn/mw1024/60c9620fjw1em9mstdddrj20ts04ojru.jpg +.. |image1| image:: http://ww4.sinaimg.cn/mw1024/60c9620fjw1em9mutbw50j20q00fbdgk.jpg + diff --git a/docs/source/tutorial-client/src/Tutorial4.rst b/docs/source/tutorial-client/src/Tutorial4.rst new file mode 100644 index 000000000..5affa6b1a --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial4.rst @@ -0,0 +1,128 @@ +Tutorial 4 注解式分布式静态配置文件和静态配置项(最佳实践) +=========================================================== + +配置类 +------ + +定义 +~~~~ + +:: + + package com.example.disconf.demo.config; + + import com.baidu.disconf.client.common.annotations.DisconfFile; + import com.baidu.disconf.client.common.annotations.DisconfFileItem; + + /** + * 静态 配置文件 示例 + * + * @author liaoqiqi + * @version 2014-6-17 + */ + @DisconfFile(filename = "static.properties") + public class StaticConfig { + + private static int staticVar; + + @DisconfFileItem(name = "staticVar", associateField = "staticVar") + public static int getStaticVar() { + return staticVar; + } + + public static void setStaticVar(int staticVar) { + StaticConfig.staticVar = staticVar; + } + + } + +使用 +~~~~ + +:: + + package com.example.disconf.demo.service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.StaticConfig; + + /** + * 使用静态配置文件的示例
+ * Plus
+ * 静态配置项 使用示例 + * + * @author liaoqiqi + * @version 2014-8-14 + */ + public class SimpleStaticService { + + private static int staticItem = 56; + + /** + * + * @return + */ + public static int getStaticFileData() { + + return StaticConfig.getStaticVar(); + } + } + +和 + +:: + + LOGGER.info("static file data:" + + SimpleStaticService.getStaticFileData()); + +配置项 +------ + +定义 +~~~~ + +:: + + package com.example.disconf.demo.service; + + import com.baidu.disconf.client.common.annotations.DisconfItem; + import com.example.disconf.demo.config.StaticConfig; + + /** + * 使用静态配置文件的示例
+ * Plus
+ * 静态配置项 使用示例 + * + * @author liaoqiqi + * @version 2014-8-14 + */ + public class SimpleStaticService { + + private static int staticItem = 56; + + /** + * + * @return + */ + public static int getStaticFileData() { + + return StaticConfig.getStaticVar(); + } + + @DisconfItem(key = "staticItem") + public static int getStaticItem() { + return staticItem; + } + + public static void setStaticItem(int staticItem) { + SimpleStaticService.staticItem = staticItem; + } + } + +使用 +~~~~ + +:: + + LOGGER.info("static item data:" + + SimpleStaticService.getStaticItem()); diff --git a/docs/source/tutorial-client/src/Tutorial5.rst b/docs/source/tutorial-client/src/Tutorial5.rst new file mode 100644 index 000000000..3bebf3a2d --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial5.rst @@ -0,0 +1,44 @@ +Tutorial 5 基于XML的分布式配置文件管理,不会自动reload +===================================================== + +在 `Tutorial 1 `__ 里, +我们实现了一个简单的Redis服务程序,它使用分布式配置进行管理,此Redis的配置文件存储在分布式服务器 +disconf-web 上。它使用的是注解式的配置管理。 + +Disconf亦支持非注解式的分布式配置管理,下面定义一下概念: + +- 非注解式(托管式):配置文件没有相应的配置注解类,此配置文件不会被注入到配置类中。disconf只是简单的对其进行“托管”。 + 启动时下载配置文件;配置文件变化时,负责动态推送。注意,此种方式,程序不会自动reload配置,需要自己写回调函数。 + +**由于此方法不会自动reload,因此,对于那些比较重的资源,比如jdbc等,是比较好的托管方式。** + +使用方法就是在你的applicationContext.xml里添加以下一段代码: + +:: + + + + + + myserver.properties + + + + + + + + + + + + + + +该配置文件列表将被Disconf全部托管。实例启动时,这些配置文件将被下载,当配置更改时,实例亦能感知到,并重新下载这些配置,并且自动调用回调函数。 + +目前支持 任意类型的 配置文件的托管。 + +注意:这些配置文件没有相应的配置文件类。 diff --git a/docs/source/tutorial-client/src/Tutorial7.rst b/docs/source/tutorial-client/src/Tutorial7.rst new file mode 100644 index 000000000..ade3407a8 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial7.rst @@ -0,0 +1,18 @@ +Tutorial 7 可自定义的部分托管的分布式配置 +========================================= + +假设我们已经将所有配置文件和配置项都使用Disconf进行托管了。 + +| 在多人开发情况下,可能会有一两个配置文件需要经常改动,且每个人的配置都不大一样,在这种情况下,当然希望此配置文件(或多个配置) +| 均不要使用Disconf托管。 + +Disconf考虑到了此种情况。举个实例,数据库配置文件,每个人的数据库可能不大一样,那么,你可以修改 +disconf.properties : + +:: + + # 忽略哪些分布式配置,用逗号分隔 + disconf.ignore=jdbc-mysql.properties + +将此配置文件添加到ignore的列表里。这样,程序运行时,Disconf就会忽略托管此配置文件,而改为读取你本地的配置文件 +jdbc-mysql.properties。 diff --git a/docs/source/tutorial-client/src/Tutorial8.rst b/docs/source/tutorial-client/src/Tutorial8.rst new file mode 100644 index 000000000..604dbeb17 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial8.rst @@ -0,0 +1,179 @@ +Tutorial 8 基于XML的分布式配置文件管理,自动reload +================================================= + +在 `Tutorial1 `__, `Tutorial2 `__, +`Tutorial3 `__, `Tutorial4 `__ +里讲到使用 注解式的分布式配置,它的特点是 + +- 优点: + + - 支持.properties配置文件 + - 支持配置项 + - 通过撰写配置类,代码结构清晰 + - 配置更新时,自动注入 + - 支持并发时配置更新统一生效 + - 无额外的XML配置,不需要在xml定义 java bean(config类) + - 代码风格 简单易懂 + +- 缺点: + + - 需要撰写配置类,代码侵入 + +在 `Tutorial5 `__ +里讲解了非注解式的分布式配置文件动态管理。它的特点是 + +- 优点: + + - 支持任意类型的配置文件 + - 无代码侵入 + +- 缺点: + + - 需要在xml定义 java bean + - 配置更新时无法自动注入java + bean里。你可以写回调函数来支持自动注入。 + +在本教程里,将讲解一种基于XML的分布式配置文件管理,它是\ `Tutorial5 `__\ 方式的一种增强,它的特点是: + +- 优点: + + - 支持任意类型的配置文件 + - 对于.properties配置文件,配置更新时,自动注入reload + - 无代码侵入 + - 适合于旧项目的迁移 + +- 缺点: + + - 需要在xml定义 java bean + - 非.properties配置更新时无法自动注入java + bean里,你可以写回调函数来支持自动注入。 + +在这里将以 disconf-standalone-demo中的 demo为例,讲解如何实现 +无代码侵入的分布式配置 + +第一步:上传配置文件 +-------------------- + +上传 autoconfig.properties 至 disconf-web里 + +第二步:修改配置文件 +-------------------- + +添加 基本的 disconf支持 + +:: + + + + + + + + + + + + +注:从版本\ ``2.6.30``\ 开始,不再需要扫描包\ ``com.baidu``\ 了,扫描自己的包即可。即: + +:: + + + + + + + + + + + + +特别的,添加 需要进行托管的 配置文件: + +:: + + + + + + classpath:/autoconfig.properties + classpath:/autoconfig2.properties + + + + + + + + + + + + + + +在这里,添加了 6个配置文件,其中有4个.properties, 2个非properties文件 + +添加需要配置的java bean +----------------------- + +:: + + + + + + + + + +java bean 与 传统的 spring 写法没有任何区别 + +补充 +---- + +如果想配置文件,但是不想自动reload,那么该怎么办? + +可以使用以下与本方法非常相似的做法: + +:: + + + + + + myserver.properties + + + + + + + + + + + + + + +在这里,myserver.properties被disconf托管,当在disconf-web上修改配置文件时,配置文件会被自动下载至本地,但是不会reload到系统里。 + +具体可参见:\ `Tutorial5 `__ + +完结 +---- + +当在disconf-web中对 4个properties文件中的任何一个文件更新时,所有 +使用这些配置文件的java bean都将自动重新注入。无需重启程序。 + +非properties文件,则需要重启程序才可以支持。当然你可以写回调函数来支持自动注入。 diff --git a/docs/source/tutorial-client/src/Tutorial9.rst b/docs/source/tutorial-client/src/Tutorial9.rst new file mode 100644 index 000000000..db8c23d69 --- /dev/null +++ b/docs/source/tutorial-client/src/Tutorial9.rst @@ -0,0 +1,47 @@ +Tutorial 9 实现真正意义上的统一上线包 +===================================== + +问题 +~~~~ + +一直以来,凡是使用 disconf的程序均需要 ``disconf.properties`` +,在这个文件里去控制 app/env/version。 + +因此,我们要部署到不同的环境中,还是需要 不同的 +``disconf.properties``\ 。 + +有一种解决方法是,通过 jenkins 来进行打包,准备多份 +``disconf.properties`` 文件。 + +解决方法 +~~~~~~~~ + +真正的解决方法是,使用 java 命令行参数 + +目前 disconf 已经支持 ``disconf.properties`` 中所有配置项 +通过参数传入方式 启动。 + +支持的配置项具体可参见: `link <../../config/client-config.html>`__ + +这样的话,未来大家只要通过 Java 参数 就可以 动态的改变启动的 +app/env/version + +standalone 启动示例 +^^^^^^^^^^^^^^^^^^^ + +:: + + java -Ddisconf.env=rd \ + -Ddisconf.enable.remote.conf=true \ + -Ddisconf.conf_server_host=127.0.0.1:8000 \ + -Dlogback.configurationFile=logback.xml \ + -Dlog4j.configuration=file:log4j.properties \ + -Djava.ext.dirs=lib \ + -Xms1g -Xmx2g -cp ampq-logback-client-0.0.1-SNAPSHOT.jar \ + com.github.knightliao.consumer.ConsumerMain >/dev/null 2>&1 & + + +这里表示使用 disconf.env=rd + +tomcat 启动示例 +^^^^^^^^^^^^^^^ diff --git a/docs/source/tutorial-client/src/config-getter.rst b/docs/source/tutorial-client/src/config-getter.rst new file mode 100644 index 000000000..b1c8fa2bf --- /dev/null +++ b/docs/source/tutorial-client/src/config-getter.rst @@ -0,0 +1,88 @@ +统一类获取任何配置数据 +====================== + +2.6.34 版本起开始支持 +--------------------- + +主要升级点 +~~~~~~~~~~ + +增加统一的类 来个性化编程式的获取任何配置数据, 目前只支持 .properties +文件 + +接口 +~~~~ + +:: + + public class DisconfDataGetter { + + private static IDisconfDataGetter iDisconfDataGetter = new DisconfDataGetterDefaultImpl(); + + /** + * 根据 分布式配置文件 获取该配置文件的所有数据,以 map形式呈现 + * + * @param fileName + * + * @return + */ + public static Map getByFile(String fileName) { + return iDisconfDataGetter.getByFile(fileName); + } + + /** + * 获取 分布式配置文件 获取该配置文件 中 某个配置项 的值 + * + * @param fileName + * @param fileItem + * + * @return + */ + public static Object getByFileItem(String fileName, String fileItem) { + return iDisconfDataGetter.getByFileItem(fileName, fileItem); + } + + /** + * 根据 分布式配置 获取其值 + * + * @param itemName + * + * @return + */ + public static Object getByItem(String itemName) { + return iDisconfDataGetter.getByItem(itemName); + } + } + +使用方式 +~~~~~~~~ + +获取 配置文件 redis.properties 的KV值: + +:: + + DisconfDataGetter.getByFile("redis.properties"); + +获取 配置文件 autoconfig.properties 的KV值: + +:: + + DisconfDataGetter.getByFile("autoconfig.properties") + +获取 配置文件 autoconfig.properties 中 key为 auto 的值: + +:: + + DisconfDataGetter.getByFile("autoconfig.properties").get("auto") + +获取 配置文件 autoconfig.properties 中 key为 auto 的值: + +:: + + DisconfDataGetter.getByFileItem("autoconfig.properties", "auto") + +获取 配置项 moneyInvest 的值: + +:: + + DisconfDataGetter.getByItem("moneyInvest"); diff --git a/docs/source/tutorial-client/src/jar-start-up.rst b/docs/source/tutorial-client/src/jar-start-up.rst new file mode 100644 index 000000000..44fb89a74 --- /dev/null +++ b/docs/source/tutorial-client/src/jar-start-up.rst @@ -0,0 +1,94 @@ +-jar jar包启动支持 +================== + +2.6.33 版本起开始支持 +--------------------- + +主要升级点 +~~~~~~~~~~ + +当使用以 -jar 方式启动的程序(非tomcat,web方式)时,例如 springboot +时,可以无缝对接(不会出现配置文件找不到的情况) + +正确的使用方式 +~~~~~~~~~~~~~~ + +:: + + + + + + classpath*:autoconfig.properties + + + + + + + + + + + + + + +细节 +~~~~ + +在未升级前,要使用spring-boot,我们的配置可能是这样的 + +:: + + + + + + file:autoconfig.properties + + + + + + + + + + + + + + +注意,我们使用 ``file:autoconfig.properties`` 而不是 +``classpath*:autoconfig.properties``\ 。这是为什么呢? + +当以 -jar +的方式启动服务时,classpath可能会找不到,spring读取配置的原则是: + +- ``classpath*:autoconfig.properties`` : 只能读取jar包内的配置文件 +- ``file:autoconfig.properties`` : 读取的当前路径下的配置文件 + +当以 java入口类 +的方式启动服务时,classpath一定是WEB-INF/classes或者target/classes目录下,spring读取配置的原则是: + +- ``classpath*:autoconfig.properties`` : + 读取的WEB-INF/classes或者target/classes目录下的配置文件 +- ``file:autoconfig.properties`` : 读取的是当前路径下的配置文件 + +在以-jar方式启动时,原则上,我们希望将配置文件放在jar包外面,所以这里采用了 +``file:autoconfig.properties``\ ,当以这种方式启动程序时,disconf将配置下载到当前路径下,程序可以找到配置,不会报错。 + +| 但当你使用IDE(eclipse or + intellij)启动时,它就报错了,说配置文件找不到了。这是因为IDE启动时,不是以-jar的方式启动的,它是以Java入口类的方式进行启动的,disconf将配置下载到target/classes +| 目录下,如果还是使用\ ``file:autoconfig.properties``\ ,程序在当前路径下是无法找到的。 + +这就产生了一个问题:我在IDE调试时,必须使用 +``classpath*:autoconfig.properties`` ,当以命令行启动时,必须使用 +``file:autoconfig.properties`` + +本次升级就是为了避免这个问题。未来配置统一是\ ``classpath*:autoconfig.properties``\ 。原理是,在以-jar启动时,通过将当前路径设置为classpath,程序启动后,disconf将配置下载到当前路径,通过读取classpath进行读取配置,就可以找到这个配置了。 diff --git a/docs/source/tutorial-web/12-open-api-for-web-client.md b/docs/source/tutorial-web/12-open-api-for-web-client.md new file mode 100644 index 000000000..0faa792f4 --- /dev/null +++ b/docs/source/tutorial-web/12-open-api-for-web-client.md @@ -0,0 +1,77 @@ +Tutorial 12 disconf-web 为客户端 开放的 Http API +======= + +## 前言 + +- 目标:让开发者具有自定义开发客户端的能力 +- 目前已经支持 java + +## 准备 + +- 1. 获取配置时是从disconf-web获取 +- 2. 得到配置更新时是从ZK上获取,得到通知后,再从disconf-web上获取配置值 + +### 获取配置接口 + +以下接口均不需要权限控制,Http-Rest 风格 + +#### /api/config/item + +- 描述:获取配置项 +- url示例: /api/config/item?app=disconf_demo&env=rd&version=1_0_0_0&key=discountRate +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|app |app值 |是| + |2|version |version值 |是| + |3|env |env值 |是| + |4|key |配置项的key |是| + +- 返回示例: + + {"status":1,"message":"","value":"0.5"} + +- curl 示例 + + ➜ disconf git:(dev) curl 'http://disconf.com/api/config/item?app=disconf_demo&env=rd&version=1_0_0_0&key=discountRate' + {"status":1,"message":"","value":"0.5"} + + +#### /api/config/file + +- 描述:获取配置文件 +- url示例: /api/config/file?app=disconf_demo&env=rd&version=1_0_0_0&key=autoconfig.properties +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|app |app值 |是| + |2|version |version值 |是| + |3|env |env值 |是| + |4|key |配置文件的key |是| + +- 返回示例: 文件 +- curl 示例 + + ➜ disconf git:(dev) curl 'http://disconf.com/api/config/file?app=disconf_demo&env=rd&version=1_0_0_0&key=autoconfig.properties' + auto=bbdxxjdccdcccdxdcdc + xx% + + +### 得到更新通知的接口 + +客户端程序需要进行订阅ZK结点 + +在上面的两个示例中,需要分别订阅的结点是: + +- /disconf/disconf_demo_1_0_0_0_rd/item/discountRate +- /disconf/disconf_demo_1_0_0_0_rd/file/autoconfig.properties + +格式是 + +- `/disconf/{{app_name}}_{{version}}_{{env}}/item/keyname` +- `/disconf/{{app_name}}_{{version}}_{{env}}/file/keyname` + diff --git a/docs/source/tutorial-web/12-open-api-for-web.md b/docs/source/tutorial-web/12-open-api-for-web.md new file mode 100644 index 000000000..8c7e03e45 --- /dev/null +++ b/docs/source/tutorial-web/12-open-api-for-web.md @@ -0,0 +1,348 @@ +Tutorial 12 disconf-web 为界面 开放的 Http API +======= + +## 前言 + +- 目标:让开发者具有自定义定制web控制台界面的能力 + +以下接口均需要权限控制 + +## app接口 + +### /api/app/list + +- 描述:返回所有app的列表 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + {"message":{},"sessionId":"3e560be1-9000-4a5c-8371-35312040d8ac","success":"true","page":{"result":[ +{"id":2,"name":"disconf_demo"}],"order":null,"orderBy":null,"totalCount":1,"pageNo":null,"pageSize":null +,"footResult":null}} + +### /api/app + +- 描述:生成一个app +- 请求类型: POST +- 参数 + + | |name |desc |是否必要| + |---|-------|-------|----| + |1 |app |app的名字 |是| + |2 |desc |描述 |是| + |3 |emails|关联的邮箱列表,逗号分隔|否| + +- 返回示例: + + {"message":{},"sessionId":"31617346-2020-4016-bc0d-f7a62d91945b","success":"true","result":"创建成功"} + +## auth接口 + +### /api/account/session + +- 描述:获取当前会话信息 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + {"message":{},"sessionId":"9d466ef4-1782-451a-8a4c-e2b99601dcba","success":"true","result":{"visitor" +:{"id":6,"name":"admin","role":null}}} + +### /api/account/signin + +- 描述:登录 +- 请求类型: POST +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|name |用户名 |是| + |2|password |密码 |是| + |3|remember|是否记住自己|是| + +- 返回示例: + + 失败示例: + + {"message":{"field":{"password":"密码不正确"}},"sessionId":"29efac2d-fec1-40c7-b0d2-8433fb8a8c2c","success" +:"false","status":2000} + + 或成功示例 + + {"message":{},"sessionId":"53e68882-bf9a-47ab-a1f6-ba347906c2a5","success":"true","result":{"visitor" +:{"id":6,"name":"admin","role":null}}} + +### /api/account/signout + +- 描述:退出 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + {"message":{},"sessionId":"e6c3134d-ba55-4e34-837d-46d08604e2b1","success":"true","result":{"ok":"ok" +}} + +## env接口 +### /api/env/list + +- 描述:返回所有环境的列表 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + {"message":{},"sessionId":"826141a9-3255-4beb-88c8-018e40981f6c","success":"true","page":{"result":[ +{"id":1,"name":"rd"},{"id":2,"name":"qa"},{"id":3,"name":"local"},{"id":4,"name":"online"}],"order":null +,"orderBy":null,"totalCount":4,"pageNo":null,"pageSize":null,"footResult":null}} + +## config接口 + +### /api/web/config/item + +- 描述:创建配置项 +- 请求类型: POST +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|key |配置项key |是| + |2|value |配置项value |是| + |3|appId |app |是| + |4|version |版本 |是| + |5|envId |环境 |是| + +- 返回示例: + + {"message":{},"sessionId":"dc7b3355-2763-4122-bb69-96d2eb282027","success":"true","result":"创建成功"} + +### /api/web/config/file + +- 描述:生成配置, 采用直接上传文件方式 +- 请求类型: POST +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|myfilerar |配置文件 |是| + |2|appId |app |是| + |3|version |版本 |是| + |4|envId |环境 |是| + +- 返回示例: + + {"message":{},"sessionId":"b6a75894-a94b-4075-a4c7-05ed0be6b016","success":"true","result":"创建成功"} + +### /api/web/config/filetext + +- 描述:生成配置, 采用直接上传文本方式 +- 请求类型: POST +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|fileName |文件名 |是| + |2|fileContent |文件内容 |是| + |3|appId |app |是| + |4|version |版本 |是| + |5|envId |环境 |是| + +- 返回示例: + + {"message":{},"sessionId":"b6a75894-a94b-4075-a4c7-05ed0be6b016","success":"true","result":"创建成功"} + +### /api/web/config/versionlist + +- 描述:根据app, env 获取所有的 版本列表 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|appId |app |是| + |2|envId |环境 |否| + +- 返回示例: + + {"message":{},"sessionId":"cd908c6a-1dba-42b4-8a6f-3cb997ffb747","success":"true","page":{"result":["1_0_0_0" +],"order":null,"orderBy":null,"totalCount":1,"pageNo":null,"pageSize":null,"footResult":null}} + +### /api/web/config/list + +- 描述:根据app, env , version 获取所有的 配置列表,含有machine size, machine list,error num 等信息 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|appId |app |是| + |2|envId |环境 |是| + |3|version |版本 |是| + +- 返回示例: + + {"message":{},"sessionId":"95839567-d098-4456-b44a-dd556454ec65","success":"true","page":{"result":[ +{"configId":148,"appName":"disconf_demo","appId":2,"version":"1_0_0_0","envId":1,"envName":"rd","type" +:"配置文件","typeId":0,"key":"autoconfig.properties","value":"auto=bbdxxjdccdcccdxdcdc\nxx","createTime" +:"20150320130619","modifyTime":"201603271140","machineSize":1,"machineList":[{"machine":"localhost_0_4b860678-290a-4bdf-9a79-2600598f419b" +,"value":"{\"auto\":\"bbdxxjdccdcccdxdcdc\",\"xx\":\"\"}","errorList":[]}],"errorNum":0},{"configId" +:149,"appName":"disconf_demo","appId":2,"version":"1_0_0_0","envId":1,"envName":"rd","type":"配置文件","typeId" +:0,"key":"autoconfig2.properties","value":"auto2=cd你好 坑爹 22fd d","createTime":"20150320130625","modifyTime" +:"201602011810","machineSize":1,"machineList":[{"machine":"localhost_0_4b860678-290a-4bdf-9a79-2600598f419b" +,"value":"{\"auto2\":\"cd你好 坑爹 22fd d\"}","errorList":[]}],"errorNum":0}..... + +### /api/web/config/simple/list + +- 描述:根据app, env , version 获取所有的 配置列表,无machine size, machine list,error num 等信息 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|appId |app |是| + |2|envId |环境 |是| + |3|version |版本 |是| + +- 返回示例: + + {"message":{},"sessionId":"ee170075-0974-4b9a-b341-f8f33beda453","success":"true","page":{"result":[ +{"configId":155,"appName":"测试","appId":3,"version":"1_0_0_0","envId":1,"envName":"rd","type":"配置文件","typeId" +:0,"key":"a.properties","value":"","createTime":"20160423115212","modifyTime":"201604231152","machineSize" +:0,"machineList":[],"errorNum":0},{"configId":154,"appName":"测试","appId":3,"version":"1_0_0_0","envId" +:1,"envName":"rd","type":"配置项","typeId":1,"key":"dd","value":"","createTime":"20160423114721","modifyTime" +:"201604231147","machineSize":0,"machineList":[],"errorNum":0}],"order":"asc","orderBy":"name","totalCount" +:2,"pageNo":null,"pageSize":null,"footResult":null}} + +### /api/web/config/{configId} + +- 描述:获取某个config的内容 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + +- 返回示例: + + {"message":{},"sessionId":"2944fb48-3735-48a0-a1bf-ad1bf4980c71","success":"true","result":{"configId" +:148,"appName":"disconf_demo","appId":2,"version":"1_0_0_0","envId":1,"envName":"rd","type":"配置文件","typeId" +:0,"key":"autoconfig.properties","value":"auto=bbdxxjdccdcccdxdcdc\nxx","createTime":"20150320130619" +,"modifyTime":"201603271140","machineSize":0,"machineList":null,"errorNum":0}} + + +### /api/web/config/zk/{configId} + +- 描述:获取该配置相对应的机器列表以及数据 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + +- 返回示例: + + {"message":{},"sessionId":"6bf69e7e-a6f7-4c42-b04e-0336c132fef2","success":"true","result":{"datalist" +:[{"machine":"localhost_0_4b860678-290a-4bdf-9a79-2600598f419b","value":"{\"auto\":\"bbdxxjdccdcccdxdcdc +\",\"xx\":\"\"}","errorList":[]}],"errorNum":0,"machineSize":1}} + +### /api/web/config/download/{configId} + +- 描述:以下载文件的形式下载配置 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + +- 返回示例: N/A + +### /api/web/config/downloadfilebatch + +- 描述:以下载文件的形式批量下载配置 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|appId |app |是| + |2|envId |环境 |是| + |3|version |版本 |是| + +- 返回示例: N/A + +### /api/web/config/item/{configId} + +- 描述:修改配置项的值 +- 请求类型: PUT +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + |1|value |value |是| + +- 返回示例: + + {"message":{},"sessionId":"004cd21f-f2d4-4754-b5c1-f215266d63c4","success":"true","result":"修改成功,邮件发 +送失败,请检查邮箱配置"} + +### /api/web/config/file/{configId} + +- 描述:以上传文件的形式 修改配置文件的值 +- 请求类型: PUT +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + |1|myfilerar |文件 |是| + +- 返回示例: {"message":{},"sessionId":"6bacbb02-faf4-416b-bf12-b33d4df328ca","success":"true","result":"修改成功,邮件发 +送失败,请检查邮箱配置"} + + +### /api/web/config/filetext/{configId} + +- 描述:以上传文件内容的形式 修改配置文件的值 +- 请求类型: PUT +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + |1|fileContent |文件内容 |是| + +- 返回示例: {"message":{},"sessionId":"6bacbb02-faf4-416b-bf12-b33d4df328ca","success":"true","result":"修改成功,邮件发 +送失败,请检查邮箱配置"} + +### /api/web/config/{configId} + +- 描述:删除配置 +- 请求类型: DELETE +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|configId |configId |是| + +- 返回示例: {"message":{},"sessionId":"b2e36172-1a60-479a-acc9-5854e3f93d98","success":"true","result":"删除成功"} + +## zk 接口 + +## /api/zoo/zkdeploy + +- 描述:获取ZK部署情况 +- 请求类型: GET +- 参数 + + |#|name |desc |是否必要| + |---|-------|-------|----| + |1|appId |app |是| + |2|envId |环境 |是| + |3|version |版本 |是| + +- 返回示例: N/A + + + diff --git a/docs/source/tutorial-web/Tutorial6.md b/docs/source/tutorial-web/Tutorial6.md new file mode 100644 index 000000000..86f24100c --- /dev/null +++ b/docs/source/tutorial-web/Tutorial6.md @@ -0,0 +1,61 @@ +Tutorial 6 disconf-web 功能详解 +======= + +## UI及架构 ## + +[disconf-web](../install/02.html) + +## 主要功能 + +###新建功能 + +- 新建APP +- 新建配置文件、新建配置项 + +###修改功能 + +- 修改配置文件 +- 修改配置项 + +###删除功能 + +- 删除配置文件 +- 删除配置项 + +### 下载功能 + +- 下载单个配置文件 +- 批量下载指定 APP、ENV、VERSION 下的所有配置文件 + +### 查询功能 + +- 查询配置功能 + - 查询指定 APP、ENV、VERSION 下的所有配置文件列表 + - 查询配置文件内容 + - 查询配置项内容 + - 查询使用此配置的所有实例列表,并且 当实例使用的配置值与中心不一致时,会有飘红提示 +- 查询ZK功能 + - 查询 指定 APP、ENV、VERSION 下的ZK信息。 + +### 权限功能 + +- 用户访问哪些APP可以控制(USER表里的 ownapps 字段) + +### 手动触发的提醒功能 + +- 当新建或修改配置时,会有邮件提醒(app表里的 email 字段) + +![](http://ww3.sinaimg.cn/mw1024/60c9620fgw1emyv9b06rpj20r40c475i.jpg) + +### 自动提醒功能 + +- 系统可以自动监控 所有实例与 配置中心的值 是否一致,如果不一致,就会报警(app表里的 email 字段) + +![](http://ww1.sinaimg.cn/mw1024/60c9620fgw1emyvhj84a4j20sq07awfc.jpg) + +## 注 + +- `手动触发的提醒功能` 与 `自动提醒功能` 两个功能的 DIFF 功能 采用的是 [java-diff](http://techv5.com/topic/979/) + + + diff --git a/docs/source/tutorial-web/index.rst b/docs/source/tutorial-web/index.rst new file mode 100644 index 000000000..c9433d81f --- /dev/null +++ b/docs/source/tutorial-web/index.rst @@ -0,0 +1,10 @@ +Tutorial-web +============ + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + src/Tutorial6 + src/12-open-api-for-web + src/12-open-api-for-web-client diff --git a/docs/source/tutorial-web/src/12-open-api-for-web-client.rst b/docs/source/tutorial-web/src/12-open-api-for-web-client.rst new file mode 100644 index 000000000..003b3782f --- /dev/null +++ b/docs/source/tutorial-web/src/12-open-api-for-web-client.rst @@ -0,0 +1,101 @@ +Tutorial 12 disconf-web 为客户端 开放的 Http API +================================================ + +前言 +---- + +- 目标:让开发者具有自定义开发客户端的能力 +- 目前已经支持 java + +准备 +---- + +- + + #. 获取配置时是从disconf-web获取 + +- + + #. 得到配置更新时是从ZK上获取,得到通知后,再从disconf-web上获取配置值 + +获取配置接口 +~~~~~~~~~~~~ + +以下接口均不需要权限控制,Http-Rest 风格 + +/api/config/item +^^^^^^^^^^^^^^^^ + +- 描述:获取配置项 +- url示例: + /api/config/item?app=disconf\_demo&env=rd&version=1\_0\_0\_0&key=discountRate +- 请求类型: GET +- 参数 + + +-----+-----------+---------------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+===============+============+ + | 1 | app | app值 | 是 | + +-----+-----------+---------------+------------+ + | 2 | version | version值 | 是 | + +-----+-----------+---------------+------------+ + | 3 | env | env值 | 是 | + +-----+-----------+---------------+------------+ + | 4 | key | 配置项的key | 是 | + +-----+-----------+---------------+------------+ + +- 返回示例: + + {"status":1,"message":"","value":"0.5"} + +- curl 示例 + + :: + + ➜ disconf git:(dev) curl 'http://disconf.com/api/config/item?app=disconf_demo&env=rd&version=1_0_0_0&key=discountRate' + {"status":1,"message":"","value":"0.5"} + +/api/config/file +^^^^^^^^^^^^^^^^ + +- 描述:获取配置文件 +- url示例: + /api/config/file?app=disconf\_demo&env=rd&version=1\_0\_0\_0&key=autoconfig.properties +- 请求类型: GET +- 参数 + + +-----+-----------+-----------------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+=================+============+ + | 1 | app | app值 | 是 | + +-----+-----------+-----------------+------------+ + | 2 | version | version值 | 是 | + +-----+-----------+-----------------+------------+ + | 3 | env | env值 | 是 | + +-----+-----------+-----------------+------------+ + | 4 | key | 配置文件的key | 是 | + +-----+-----------+-----------------+------------+ + +- 返回示例: 文件 +- curl 示例 + + :: + + ➜ disconf git:(dev) curl 'http://disconf.com/api/config/file?app=disconf_demo&env=rd&version=1_0_0_0&key=autoconfig.properties' + auto=bbdxxjdccdcccdxdcdc + xx% + +得到更新通知的接口 +~~~~~~~~~~~~~~~~~~ + +客户端程序需要进行订阅ZK结点 + +在上面的两个示例中,需要分别订阅的结点是: + +- /disconf/disconf\_demo\_1\_0\_0\_0\_rd/item/discountRate +- /disconf/disconf\_demo\_1\_0\_0\_0\_rd/file/autoconfig.properties + +格式是 + +- ``/disconf/{{app_name}}_{{version}}_{{env}}/item/keyname`` +- ``/disconf/{{app_name}}_{{version}}_{{env}}/file/keyname`` diff --git a/docs/source/tutorial-web/src/12-open-api-for-web.rst b/docs/source/tutorial-web/src/12-open-api-for-web.rst new file mode 100644 index 000000000..59e13bc32 --- /dev/null +++ b/docs/source/tutorial-web/src/12-open-api-for-web.rst @@ -0,0 +1,438 @@ +Tutorial 12 disconf-web 为界面 开放的 Http API +============================================== + +前言 +---- + +- 目标:让开发者具有自定义定制web控制台界面的能力 + +以下接口均需要权限控制 + +app接口 +------- + +/api/app/list +~~~~~~~~~~~~~ + +- 描述:返回所有app的列表 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + | {"message":{},"sessionId":"3e560be1-9000-4a5c-8371-35312040d8ac","success":"true","page":{"result":[ + | {"id":2,"name":"disconf\_demo"}],"order":null,"orderBy":null,"totalCount":1,"pageNo":null,"pageSize":null + | ,"footResult":null}} + +/api/app +~~~~~~~~ + +- 描述:生成一个app +- 请求类型: POST +- 参数 + + +-----+----------+----------------------------+------------+ + | | name | desc | 是否必要 | + +=====+==========+============================+============+ + | 1 | app | app的名字 | 是 | + +-----+----------+----------------------------+------------+ + | 2 | desc | 描述 | 是 | + +-----+----------+----------------------------+------------+ + | 3 | emails | 关联的邮箱列表,逗号分隔 | 否 | + +-----+----------+----------------------------+------------+ + +- 返回示例: + + {"message":{},"sessionId":"31617346-2020-4016-bc0d-f7a62d91945b","success":"true","result":"创建成功"} + +auth接口 +-------- + +/api/account/session +~~~~~~~~~~~~~~~~~~~~ + +- 描述:获取当前会话信息 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + | {"message":{},"sessionId":"9d466ef4-1782-451a-8a4c-e2b99601dcba","success":"true","result":{"visitor" + | :{"id":6,"name":"admin","role":null}}} + +/api/account/signin +~~~~~~~~~~~~~~~~~~~ + +- 描述:登录 +- 请求类型: POST +- 参数 + + +-----+------------+----------------+------------+ + | # | name | desc | 是否必要 | + +=====+============+================+============+ + | 1 | name | 用户名 | 是 | + +-----+------------+----------------+------------+ + | 2 | password | 密码 | 是 | + +-----+------------+----------------+------------+ + | 3 | remember | 是否记住自己 | 是 | + +-----+------------+----------------+------------+ + +- 返回示例: + + 失败示例: + + | {"message":{"field":{"password":"密码不正确"}},"sessionId":"29efac2d-fec1-40c7-b0d2-8433fb8a8c2c","success" + | :"false","status":2000} + + 或成功示例 + + | {"message":{},"sessionId":"53e68882-bf9a-47ab-a1f6-ba347906c2a5","success":"true","result":{"visitor" + | :{"id":6,"name":"admin","role":null}}} + +/api/account/signout +~~~~~~~~~~~~~~~~~~~~ + +- 描述:退出 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + | {"message":{},"sessionId":"e6c3134d-ba55-4e34-837d-46d08604e2b1","success":"true","result":{"ok":"ok" + | }} + +env接口 +------- + +/api/env/list +~~~~~~~~~~~~~ + +- 描述:返回所有环境的列表 +- 请求类型: GET +- 参数示例:N/A +- 返回示例: + + | {"message":{},"sessionId":"826141a9-3255-4beb-88c8-018e40981f6c","success":"true","page":{"result":[ + | {"id":1,"name":"rd"},{"id":2,"name":"qa"},{"id":3,"name":"local"},{"id":4,"name":"online"}],"order":null + | ,"orderBy":null,"totalCount":4,"pageNo":null,"pageSize":null,"footResult":null}} + +config接口 +---------- + +/api/web/config/item +~~~~~~~~~~~~~~~~~~~~ + +- 描述:创建配置项 +- 请求类型: POST +- 参数 + + +-----+-----------+---------------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+===============+============+ + | 1 | key | 配置项key | 是 | + +-----+-----------+---------------+------------+ + | 2 | value | 配置项value | 是 | + +-----+-----------+---------------+------------+ + | 3 | appId | app | 是 | + +-----+-----------+---------------+------------+ + | 4 | version | 版本 | 是 | + +-----+-----------+---------------+------------+ + | 5 | envId | 环境 | 是 | + +-----+-----------+---------------+------------+ + +- 返回示例: + + {"message":{},"sessionId":"dc7b3355-2763-4122-bb69-96d2eb282027","success":"true","result":"创建成功"} + +/api/web/config/file +~~~~~~~~~~~~~~~~~~~~ + +- 描述:生成配置, 采用直接上传文件方式 +- 请求类型: POST +- 参数 + + +-----+-------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+=============+============+============+ + | 1 | myfilerar | 配置文件 | 是 | + +-----+-------------+------------+------------+ + | 2 | appId | app | 是 | + +-----+-------------+------------+------------+ + | 3 | version | 版本 | 是 | + +-----+-------------+------------+------------+ + | 4 | envId | 环境 | 是 | + +-----+-------------+------------+------------+ + +- 返回示例: + + {"message":{},"sessionId":"b6a75894-a94b-4075-a4c7-05ed0be6b016","success":"true","result":"创建成功"} + +/api/web/config/filetext +~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:生成配置, 采用直接上传文本方式 +- 请求类型: POST +- 参数 + + +-----+---------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+===============+============+============+ + | 1 | fileName | 文件名 | 是 | + +-----+---------------+------------+------------+ + | 2 | fileContent | 文件内容 | 是 | + +-----+---------------+------------+------------+ + | 3 | appId | app | 是 | + +-----+---------------+------------+------------+ + | 4 | version | 版本 | 是 | + +-----+---------------+------------+------------+ + | 5 | envId | 环境 | 是 | + +-----+---------------+------------+------------+ + +- 返回示例: + + {"message":{},"sessionId":"b6a75894-a94b-4075-a4c7-05ed0be6b016","success":"true","result":"创建成功"} + +/api/web/config/versionlist +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:根据app, env 获取所有的 版本列表 +- 请求类型: GET +- 参数 + + +-----+---------+--------+------------+ + | # | name | desc | 是否必要 | + +=====+=========+========+============+ + | 1 | appId | app | 是 | + +-----+---------+--------+------------+ + | 2 | envId | 环境 | 否 | + +-----+---------+--------+------------+ + +- 返回示例: + + {"message":{},"sessionId":"cd908c6a-1dba-42b4-8a6f-3cb997ffb747","success":"true","page":{"result":["1\_0\_0\_0"],"order":null,"orderBy":null,"totalCount":1,"pageNo":null,"pageSize":null,"footResult":null}} + +/api/web/config/list +~~~~~~~~~~~~~~~~~~~~ + +- 描述:根据app, env , version 获取所有的 配置列表,含有machine size, + machine list,error num 等信息 +- 请求类型: GET +- 参数 + + +-----+-----------+--------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+========+============+ + | 1 | appId | app | 是 | + +-----+-----------+--------+------------+ + | 2 | envId | 环境 | 是 | + +-----+-----------+--------+------------+ + | 3 | version | 版本 | 是 | + +-----+-----------+--------+------------+ + +- 返回示例: + + | {"message":{},"sessionId":"95839567-d098-4456-b44a-dd556454ec65","success":"true","page":{"result":[ + | {"configId":148,"appName":"disconf\_demo","appId":2,"version":"1\_0\_0\_0","envId":1,"envName":"rd","type" + | :"配置文件","typeId":0,"key":"autoconfig.properties","value":"auto=bbdxxjdccdcccdxdcdc\\nxx","createTime" + | :"20150320130619","modifyTime":"201603271140","machineSize":1,"machineList":[{"machine":"localhost\_0\_4b860678-290a-4bdf-9a79-2600598f419b" + | ,"value":"{"auto":"bbdxxjdccdcccdxdcdc","xx":""}","errorList":[]}],"errorNum":0},{"configId" + | :149,"appName":"disconf\_demo","appId":2,"version":"1\_0\_0\_0","envId":1,"envName":"rd","type":"配置文件","typeId" + | :0,"key":"autoconfig2.properties","value":"auto2=cd你好 坑爹 22fd + d","createTime":"20150320130625","modifyTime" + | :"201602011810","machineSize":1,"machineList":[{"machine":"localhost\_0\_4b860678-290a-4bdf-9a79-2600598f419b" + | ,"value":"{"auto2":"cd你好 坑爹 22fd + d"}","errorList":[]}],"errorNum":0}..... + +/api/web/config/simple/list +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:根据app, env , version 获取所有的 配置列表,无machine size, + machine list,error num 等信息 +- 请求类型: GET +- 参数 + + +-----+-----------+--------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+========+============+ + | 1 | appId | app | 是 | + +-----+-----------+--------+------------+ + | 2 | envId | 环境 | 是 | + +-----+-----------+--------+------------+ + | 3 | version | 版本 | 是 | + +-----+-----------+--------+------------+ + +- 返回示例: + + | {"message":{},"sessionId":"ee170075-0974-4b9a-b341-f8f33beda453","success":"true","page":{"result":[ + | {"configId":155,"appName":"测试","appId":3,"version":"1\_0\_0\_0","envId":1,"envName":"rd","type":"配置文件","typeId" + | :0,"key":"a.properties","value":"","createTime":"20160423115212","modifyTime":"201604231152","machineSize" + | :0,"machineList":[],"errorNum":0},{"configId":154,"appName":"测试","appId":3,"version":"1\_0\_0\_0","envId" + | :1,"envName":"rd","type":"配置项","typeId":1,"key":"dd","value":"","createTime":"20160423114721","modifyTime" + | :"201604231147","machineSize":0,"machineList":[],"errorNum":0}],"order":"asc","orderBy":"name","totalCount" + | :2,"pageNo":null,"pageSize":null,"footResult":null}} + +/api/web/config/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:获取某个config的内容 +- 请求类型: GET +- 参数 + + +-----+------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+============+============+============+ + | 1 | configId | configId | 是 | + +-----+------------+------------+------------+ + +- 返回示例: + + | {"message":{},"sessionId":"2944fb48-3735-48a0-a1bf-ad1bf4980c71","success":"true","result":{"configId" + | :148,"appName":"disconf\_demo","appId":2,"version":"1\_0\_0\_0","envId":1,"envName":"rd","type":"配置文件","typeId" + | :0,"key":"autoconfig.properties","value":"auto=bbdxxjdccdcccdxdcdc\\nxx","createTime":"20150320130619" + | ,"modifyTime":"201603271140","machineSize":0,"machineList":null,"errorNum":0}} + +/api/web/config/zk/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:获取该配置相对应的机器列表以及数据 +- 请求类型: GET +- 参数 + + +-----+------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+============+============+============+ + | 1 | configId | configId | 是 | + +-----+------------+------------+------------+ + +- 返回示例: + + | {"message":{},"sessionId":"6bf69e7e-a6f7-4c42-b04e-0336c132fef2","success":"true","result":{"datalist" + | :[{"machine":"localhost\_0\_4b860678-290a-4bdf-9a79-2600598f419b","value":"{"auto":"bbdxxjdccdcccdxdcdc + | ","xx":""}","errorList":[]}],"errorNum":0,"machineSize":1}} + +/api/web/config/download/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:以下载文件的形式下载配置 +- 请求类型: GET +- 参数 + + +-----+------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+============+============+============+ + | 1 | configId | configId | 是 | + +-----+------------+------------+------------+ + +- 返回示例: N/A + +/api/web/config/downloadfilebatch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:以下载文件的形式批量下载配置 +- 请求类型: GET +- 参数 + + +-----+-----------+--------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+========+============+ + | 1 | appId | app | 是 | + +-----+-----------+--------+------------+ + | 2 | envId | 环境 | 是 | + +-----+-----------+--------+------------+ + | 3 | version | 版本 | 是 | + +-----+-----------+--------+------------+ + +- 返回示例: N/A + +/api/web/config/item/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:修改配置项的值 +- 请求类型: PUT +- 参数 + + +-----+------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+============+============+============+ + | 1 | configId | configId | 是 | + +-----+------------+------------+------------+ + | 1 | value | value | 是 | + +-----+------------+------------+------------+ + +- 返回示例: + + | {"message":{},"sessionId":"004cd21f-f2d4-4754-b5c1-f215266d63c4","success":"true","result":"修改成功,邮件发 + | 送失败,请检查邮箱配置"} + +/api/web/config/file/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:以上传文件的形式 修改配置文件的值 +- 请求类型: PUT +- 参数 + + +-----+-------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+=============+============+============+ + | 1 | configId | configId | 是 | + +-----+-------------+------------+------------+ + | 1 | myfilerar | 文件 | 是 | + +-----+-------------+------------+------------+ + +- | 返回示例: + {"message":{},"sessionId":"6bacbb02-faf4-416b-bf12-b33d4df328ca","success":"true","result":"修改成功,邮件发 + | 送失败,请检查邮箱配置"} + +/api/web/config/filetext/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:以上传文件内容的形式 修改配置文件的值 +- 请求类型: PUT +- 参数 + + +-----+---------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+===============+============+============+ + | 1 | configId | configId | 是 | + +-----+---------------+------------+------------+ + | 1 | fileContent | 文件内容 | 是 | + +-----+---------------+------------+------------+ + +- | 返回示例: + {"message":{},"sessionId":"6bacbb02-faf4-416b-bf12-b33d4df328ca","success":"true","result":"修改成功,邮件发 + | 送失败,请检查邮箱配置"} + +/api/web/config/{configId} +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 描述:删除配置 +- 请求类型: DELETE +- 参数 + + +-----+------------+------------+------------+ + | # | name | desc | 是否必要 | + +=====+============+============+============+ + | 1 | configId | configId | 是 | + +-----+------------+------------+------------+ + +- 返回示例: + {"message":{},"sessionId":"b2e36172-1a60-479a-acc9-5854e3f93d98","success":"true","result":"删除成功"} + +zk 接口 +------- + +/api/zoo/zkdeploy +----------------- + +- 描述:获取ZK部署情况 +- 请求类型: GET +- 参数 + + +-----+-----------+--------+------------+ + | # | name | desc | 是否必要 | + +=====+===========+========+============+ + | 1 | appId | app | 是 | + +-----+-----------+--------+------------+ + | 2 | envId | 环境 | 是 | + +-----+-----------+--------+------------+ + | 3 | version | 版本 | 是 | + +-----+-----------+--------+------------+ + +- 返回示例: N/A diff --git a/docs/source/tutorial-web/src/Tutorial6.rst b/docs/source/tutorial-web/src/Tutorial6.rst new file mode 100644 index 000000000..a876dc831 --- /dev/null +++ b/docs/source/tutorial-web/src/Tutorial6.rst @@ -0,0 +1,79 @@ +Tutorial 6 disconf-web 功能详解 +=============================== + +UI及架构 +-------- + +`disconf-web <../install/02.html>`__ + +主要功能 +-------- + +新建功能 +~~~~~~~~ + +- 新建APP +- 新建配置文件、新建配置项 + +修改功能 +~~~~~~~~ + +- 修改配置文件 +- 修改配置项 + +删除功能 +~~~~~~~~ + +- 删除配置文件 +- 删除配置项 + +下载功能 +~~~~~~~~ + +- 下载单个配置文件 +- 批量下载指定 APP、ENV、VERSION 下的所有配置文件 + +查询功能 +~~~~~~~~ + +- 查询配置功能 + + - 查询指定 APP、ENV、VERSION 下的所有配置文件列表 + - 查询配置文件内容 + - 查询配置项内容 + - 查询使用此配置的所有实例列表,并且 + 当实例使用的配置值与中心不一致时,会有飘红提示 + +- 查询ZK功能 + + - 查询 指定 APP、ENV、VERSION 下的ZK信息。 + +权限功能 +~~~~~~~~ + +- 用户访问哪些APP可以控制(USER表里的 ownapps 字段) + +手动触发的提醒功能 +~~~~~~~~~~~~~~~~~~ + +- 当新建或修改配置时,会有邮件提醒(app表里的 email 字段) + +|image0| + +自动提醒功能 +~~~~~~~~~~~~ + +- 系统可以自动监控 所有实例与 配置中心的值 + 是否一致,如果不一致,就会报警(app表里的 email 字段) + +|image1| + +注 +-- + +- ``手动触发的提醒功能`` 与 ``自动提醒功能`` 两个功能的 DIFF 功能 + 采用的是 `java-diff `__ + +.. |image0| image:: http://ww3.sinaimg.cn/mw1024/60c9620fgw1emyv9b06rpj20r40c475i.jpg +.. |image1| image:: http://ww1.sinaimg.cn/mw1024/60c9620fgw1emyvhj84a4j20sq07awfc.jpg + diff --git a/pom.xml b/pom.xml index e0d1236a6..04dcbd427 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.baidu.disconf disconf-base - 2.6.35 + 2.6.36 pom @@ -25,9 +25,9 @@ - 2.6.35 - 2.6.35 - 2.6.35 + 2.6.36 + 2.6.36 + 2.6.36 UTF-8