配置中心-Apollo
<h2>1、什么是配置中心</h2>
<p>配置中心用来集中管理应用不同环境(dev、fat、uat、pro...)、不同集群的配置,配置修改后能够实时推送到应用端。
配置中心产生的背景以及要解决的问题:
1、细微的修改点带来的全局发布问题
2、核心加密数据对开发人员的不可见性
3、动态运维、参数配置</p>
<h2>2、微服务体系中为什么引入配置中心</h2>
<p>随着业务的发展、微服务架构的升级,服务的数量、程序的配置日益增多(各种微服务、各种服务器地址、各种参数),传统的配置文件方式和数据库的方式已无法满足开发人员对配置管理的要求:</p>
<p>安全性:配置跟随源代码保存在代码库中,容易造成配置泄漏
时效性:修改配置,需要重启服务才能生效
局限性:无法支持动态调整:例如日志开关、功能开关
因此,我们需要配置中心来统一管理配置。</p>
<h2>3、配置中心的要素</h2>
<p>1.配置是可分离的,可从微服务中抽离出来,任何的配置修改不需要动一行代码。</p>
<p>2.配置应该是中央的 通过统一的中央配置平台去配置管理不同的微服务</p>
<p>3.配置中心必须必须可靠切稳定地提供配置服务。</p>
<p>4.配置是可追溯的,任何的配置历史都是可追溯,被管理且可用。</p>
<h2>3、配置中心之Apollo</h2>
<h3>3.1、Apollo介绍</h3>
<p>Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。</p>
<p>项目地址:<a href="https://github.com/ctripcorp/apollo">https://github.com/ctripcorp/apollo</a></p>
<h3>3.2、Apollo配置中心原理</h3>
<p>架构设计:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/b740133b22b0211f7856871e89738f2e?showdoc=.jpg" alt="" />
上图简要描述了Apollo的总体设计,我们可以从下往上看:</p>
<ul>
<li>Config Service提供配置的读取、推送等功能,服务对象是Apollo客户端</li>
<li>Admin Service提供配置的修改、发布等功能,服务对象是Apollo Portal(管理界面)</li>
<li>Config Service和Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳</li>
<li>在Eureka之上我们架了一层Meta Server用于封装Eureka的服务发现接口</li>
<li>Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试</li>
<li>Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重试</li>
<li>为了简化部署,实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中</li>
</ul>
<p>工作原理:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/224fae71a2a6723bf5cb988d90f1b0e2?showdoc=.jpg" alt="" />
上图简要描述了Apollo客户端的实现原理:</p>
<p>1、客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。
2、客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
  2.1、这是一个fallback机制,为了防止推送机制失效导致配置不更新
  2.2、客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
  2.3、定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
3、客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
4、客户端会把从服务端获取到的配置在本地文件系统缓存一份
在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
5、应用程序从Apollo客户端获取最新的配置、订阅配置更新通知</p>
<h2>环境安装</h2>
<h3>创建数据库</h3>
<p>Apollo服务端共需要两个数据库:ApolloPortalDB和ApolloConfigDB。</p>
<p>需要注意的是ApolloPortalDB只需要在生产环境部署一个即可,而ApolloConfigDB需要在每个环境部署一套,如fat、uat和pro分别部署3套ApolloConfigDB。</p>
<h3>上传文件并解压</h3>
<p>注:在真实环境搭建portal可以单独一台服务器,adminservice、configservice可搭建集群在N台服务器(如:dev、pro、fat等各搭建一套)上面搭建
1、创建文件夹
mkdir adminservice
mkdir configservice
mkdir portal
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/0d00afe8100c259ee802a70141dea2c6?showdoc=.jpg" alt="" /></p>
<p>2、rz就可以上传了,解压可以用unzip命令
unzip apollo-adminservice-1.6.1-github.zip -d ./adminservice
unzip apollo-configservice-1.6.1-github.zip -d ./configservice
unzip apollo-portal-1.6.1-github.zip -d ./portal
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/3a302355195f9958161dcd8dbdb23722?showdoc=.jpg" alt="" /></p>
<h3>配置文件修改</h3>
<p>Config文件夹下application-github.properties,修改对应的数据库源信息
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/3a67cb8e19197454d0e2928be7c92d79?showdoc=.jpg" alt="" /></p>
<h3>服务启动</h3>
<p>启动顺序:各个环境的configservice、adminservice先启动,再启动portal
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/d56645be498bfa42a20b8289868c5f7a?showdoc=.jpg" alt="" /></p>
<h3>配置管理界面使用</h3>
<p>1、启动好之后,可通过http://10.1.4.70:8070/ 访问管理配置界面(默认登录用户名:apollo 密码:admin):
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/7de4e2f4bb1e242d1f532cbc517077ff?showdoc=.jpg" alt="" /></p>
<p>2、创建项目
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/845b16d79cdaecc0bf42ad27ade97de9?showdoc=.jpg" alt="" /></p>
<p>3、项目管理界面
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/810b7c47fbcc194533e1ec42edbf09ac?showdoc=.jpg" alt="" /></p>
<ul>
<li>namespce分公有、私有:
公有:始终对所有用户可见
私有:只有该项目的管理员或拥有该namespace的编辑或发布权限的用户才能看到该私有namespace的配置信息和发布历史;</li>
<li>对namespace点击“发布”之后,会进行推送更新变更给客户端;</li>
<li>可通过回滚、发布历史找回之前的版本记录;</li>
<li>授权部分用户权限、灰度发布等提供多维度的操作</li>
</ul>
<h2>场景预览</h2>
<blockquote>
<p>1、如图:timeout的属性参数值为1000</p>
</blockquote>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/aeedef2331cdf188169c9a7abe741a0f?showdoc=.jpg" alt="" /></p>
<blockquote>
<p>2、apollo更改参数值为200:</p>
</blockquote>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/b1e9f30be058685695065c043c8de736?showdoc=.jpg" alt="" /></p>
<blockquote>
<p>3、测试代码中获取到的参数值变化:</p>
</blockquote>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/ee6f49e0f9c8c7aac5cf91ae65686a62?showdoc=.jpg" alt="" /></p>
<h2>快速入门</h2>
<blockquote>
<p>1、项目加入依赖</p>
</blockquote>
<pre><code class="language-java"><!--apollo-->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
</dependency></code></pre>
<blockquote>
<p>2、配置中增加Apollo配置</p>
</blockquote>
<pre><code class="language-java"># 阿波罗配置
app:
#标识应用身份的唯一id,与apollo上的AppId需一致
id: ${spring.application.name}
apollo:
#配置中心路径
meta: http://10.1.4.70:8080
bootstrap:
enabled: true
# 指定阿波罗中配置项名称,多个用逗号隔开
namespaces: application</code></pre>
<blockquote>
<p>3、代码中使用</p>
</blockquote>
<p>方式一:</p>
<pre><code class="language-java">@Value("${timeout: 100}")
private String timeout;</code></pre>
<p>方式二:</p>
<pre><code class="language-java">@ApolloConfig("testyml.yml")
private Config ymlConfig;</code></pre>
<p>引用:</p>
<pre><code class="language-java">String testbb = ymlConfig.getProperty("testbb", "1");</code></pre>
<p>方式三:
@ConfigurationProperties的配置文件不能够实时获取到apollo的最新配置,代码中需要结合apollo提供的@ApolloConfigChangeListener监听事件获取去获取到更新变化,如下示例:
a、如下配置文件:</p>
<pre><code class="language-java">@Component("configurationProperiesTest")
@ConfigurationProperties(prefix="test")
@RefreshScope
public class ConfigurationProperiesTest {
//上传路径
private String url;
private String user;
private String pwd;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
......
}</code></pre>
<p>b、监听实时刷新</p>
<pre><code class="language-java">@Component
public class SpringBootApolloRefreshConfig {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final ConfigurationProperiesTest configurationProperiesTest;
private final RefreshScope refreshScope;
public SpringBootApolloRefreshConfig(final ConfigurationProperiesTest configurationProperiesTest,
final RefreshScope refreshScope) {
this.configurationProperiesTest = configurationProperiesTest;
this.refreshScope = refreshScope;
}
@ApolloConfigChangeListener(value = {ConfigConsts.NAMESPACE_APPLICATION},
interestedKeyPrefixes = {"test."})
public void onChange(ConfigChangeEvent changeEvent) {
logger.info("before refresh {}", configurationProperiesTest.toString());
refreshScope.refresh("configurationProperiesTest");
logger.info("after refresh {}", configurationProperiesTest.toString());
}
}</code></pre>