Apollo配置中心

2023-05-16

1. Apollo 介绍

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

服务端基于 Spring Boot 和 Spring Cloud 开发,打包后可以直接运行,不需要额外安装 Tomcat 等应用容器。Java 客户端不依赖任何框架,能够运行于所有 Java 运行时环境,同时对 Spring/Spring Boot 环境也有较好的支持。

官方给出的 Apollo 基础模型非常简单:

上图简要描述了Apollo客户端的实现原理:

① 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)

② 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。

  • 这是一个fallback机制,为了防止推送机制失效导致配置不更新
  • 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
  • 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。

③ 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中

④ 客户端会把从服务端获取到的配置在本地文件系统缓存一份

  • 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置

⑤ 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知

官方给出的架构图如下:

上图简要描述了 Apollo 的总体设计,从下往上看:

  • Config Service 提供配置的读取、推送等功能,服务对象是 Apollo 客户端

  • Admin Service 提供配置的修改、发布等功能,服务对象是 Apollo Portal(Apollo 发布管理界面)

  • 通过 Apollo 的发布界面可以多环境、集群管理配置

  • Config Service 和 Admin Service 都是多实例、无状态部署,所以需要将自己注册到Eureka 中并保持心跳,在 Eureka 之上架了一层 Meta Server 用于封装 Eureka 的服务发现接口。Apollo提供了MetaServiceProvider SPI,用户可以注入自己的MetaServiceProvider来自定义Meta Server定位逻辑

  • Client 通过域名访问 Meta Server 获取 Config Service 服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Client 侧会做 load balance、错误重试

为了兼容别的非Java应用场景,且Eureka(包括Ribbon软负载)原生仅支持Java客户端,如果要为多语言开发Eureka/Ribbon客户端,这个工作量很大也不可控。所以Apollo的作者引入了MetaServer这个角色,它是一个Eureka的Proxy,将Eureka的服务发现接口以更简单明确的HTTP接口的形式暴露出来,方便Client/Protal通过简单的HTTPClient就可以查询到Config/AdminService的地址列表。获取到服务实例地址列表之后,再以简单的客户端软负载(Client SLB)策略路由定位到目标实例,并发起调用。

1.1 Apollo Meta Server

Apollo 支持应用在不同的环境有不同的配置,所以需要在运行提供给 Apollo 客户端当前环境的 Apollo Meta Server 信息。默认情况下,meta server 和 config service 是部署在同一个 JVM 进程,所以 meta server 的地址就是 config service 的地址。

配置 apollo meta server 信息有多重方式,这里介绍两种(其他的参照官网):

1)在 Spring Boot 的 application.properties 或 bootstrap.properties 中指定 apollo.meta = http://config-service-url

  

2)通过 server.properties 配置文件

  • 可以在 server.properties 配置文件中指定 apollo.meta=http://config-service-url
  • 对于Mac/Linux,文件位置为/opt/settings/server.properties

  

  • 对于Windows,文件位置为C:\opt\settings\server.properties

1.2 本地缓存路径

Apollo 客户端(Java 项目所运行的服务器)会把从服务端获取到的配置在本地文件系统缓存一份,用于在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置,不影响应用正常运行。

本地缓存路径默认位于以下路径,所以请确保 /opt/data 或 C:\opt\data\ 目录存在,且应用有读写权限。

  • Mac/Linux: /opt/data/{appId}/config-cache
  • Windows: C:\opt\data\{appId}\config-cache

本地配置文件会以下面的文件名格式放置于本地缓存路径下:{appId}+{cluster}+{namespace}.properties

  • appId就是应用自己的appId,如100004458
  • cluster就是应用使用的集群,一般在本地模式下没有做过配置的话,就是default
  • namespace就是应用使用的配置namespace,一般是application client-local-cache

文件内容以 properties 格式存储,比如如果有两个key,一个是request.timeout,另一个是batch,那么文件内容就是如下格式:

request.timeout = 2000
batch = 2000

自定义缓存路径

1.0.0 版本开始支持自定义缓存路径,这里介绍通过Spring Boot的配置文件修改:可以在 Spring Boot 的 application.properties 或bootstrap.properties 中指定 apollo.cacheDir = /opt/data/some-cache-dir

2. Apollo 使用

(前提:Apollo 服务已经搭建成功)

2.1 新建项目

打开apollo-portal主页,点击“创建项目”。

输入项目信息

  • 部门:选择应用所在的部门
  • 应用 AppId:用来标识应用身份的唯一 id,格式为 string,需要和客户端 app.properties 中配置的 app.id 对应
  • 应用名称:应用名,仅用于界面展示
  • 应用负责人:选择的人默认会成为该项目的管理员,具备项目权限管理、集群创建、Namespace 创建等权限

点击提交。创建成功后,会自动跳转到项目首页:

2.2 分配项目权限

1)项目管理员权限

项目管理员拥有以下权限:

  • 可以管理项目的权限分配
  • 可以创建集群
  • 可以创建Namespace

创建项目时填写的应用负责人默认会成为项目的管理员之一,如果还需要其他人也成为项目管理员,可以按照下面步骤操作:

① 点击页面左侧的“管理项目”

② 搜索需要添加的成员并点击添加

2)配置编辑、发布权限

配置权限分为编辑和发布:

  • 编辑权限允许用户在Apollo界面上创建、修改、删除配置:配置修改后只在Apollo界面上变化,不会影响到应用实际使用的配置。
  • 发布权限允许用户在Apollo界面上发布、回滚配置:配置只有在发布、回滚动作后才会被应用实际使用到;Apollo在用户操作发布、回滚动作后实时通知到应用,并使最新配置生效。

项目创建完,默认没有分配配置的编辑和发布权限,需要项目管理员进行授权。

① 点击application这个namespace的授权按钮

② 分配权限

2.3 添加配置项

编辑配置需要拥有这个Namespace的编辑权限,如果发现没有新增配置按钮,可以找项目管理员授权。

① 通过表格添加

② 通过文本模式编辑(可从 Java 客户端的配置文件中)

2.4 发布配置

配置只有在发布后才会真的被应用使用到,所以在编辑完配置后,需要发布配置。

发布配置需要拥有这个Namespace的发布权限,如果发现没有发布按钮,可以找项目管理员授权。

3. Java 项目读取配置

官网:Java客户端使用指南 · apolloconfig/apollo Wiki · GitHub

Apollo 客户端依赖于 AppId,Apollo Meta Server 等环境信息来工作

3.1 添加 apollo 依赖

		<dependency>
			<groupId>com.ctrip.framework.apollo</groupId>
			<artifactId>apollo-client</artifactId>
			<version>1.5.1</version>
		</dependency>

3.2 主启动类加 @EnableApolloConfig 注解

3.3 配置文件 application.yml

igi:
  profiles:
    active: uat
apollo:
  bootstrap:
    enabled: true
    namespaces: application
  # uat
  meta: http://10.***.***.***:10000
  cacheDir: /srv/apollo
app:
  id: *_server

 应用配置文件:

3.4 读取配置项

    @Value("${mapperLocations:#{null}}")
    private  String mapperLocations;

也可以添加注解批量读取:

@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisCacheProperties {
    private String keyPrefix;
    private long timeToLive;
}

3.5 Apollo监听配置变化事件

import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * Apollo 配置动态监听
 */
@Component
@Slf4j
public class ApolloChangeConfiguration {

    @ApolloConfigChangeListener({"application","application-common"}) //这个注解的value注解如果不写默认是application,但是我这里还加载了一个common的配置,所以加了namespace,因为只有一个参数,这里的value省略了
    public void onChange(ConfigChangeEvent changeEvent) {
        changeEvent.changedKeys().forEach(s -> {
                ConfigChange configChange = changeEvent.getChange(s);
                log.info("apollo key={},修改前的值 ={},修改后的值={}", s, configChange.getOldValue(), configChange.getNewValue());

        });
    }

}

下面这个主要是监听数据库、redis等那种项目一启动就初始化的配置,监听到配置改了之后就重新构建数据源。

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
import java.util.Collections;
import org.springframework.context.ApplicationContext;
import javax.sql.DataSource;

/**
 * @Description 监听配置变化事件
 * @Date 2020/7/28
 * @Author Janet
 */
@Component
public class ApolloChangeConfiguration  {

    private Log logger = LogFactory.getLog(this.getClass());

    private final static String DATASOURCE_TAG = "db";

    @Autowired
    ApplicationContext context;

    @ApolloConfig
    Config config;

    @Bean
    public DynamicDataSource dynamicDataSource() {
        DynamicDataSource source = new DynamicDataSource();
        source.setTargetDataSources(Collections.singletonMap(DATASOURCE_TAG, dataSource()));
        return source;
    }

    @ApolloConfigChangeListener(interestedKeyPrefixes = {"spring.datasource","spring.rabbitmq","spring.redis"})
    public void onChange(ConfigChangeEvent changeEvent) {
//        changeEvent.changedKeys().forEach(s -> {
            DynamicDataSource source = context.getBean(DynamicDataSource.class);
            source.setTargetDataSources(Collections.singletonMap(DATASOURCE_TAG, dataSource()));
            source.afterPropertiesSet();
            logger.info(String.format("动态切换数据源为:-:{change:%s}", config.getProperty("spring.datasource.url", "")));
//        });
    }

    public DataSource dataSource() {
        DataSourceProperties dataSource = new DataSourceProperties();
        dataSource.setUrl(config.getProperty("spring.datasource.url", "")); //第二个参数为 defaultValue
        dataSource.setUsername(config.getProperty("spring.datasource.username", ""));
        dataSource.setPassword(config.getProperty("spring.datasource.password", ""));
//        System.out.println("dataSource--------------dataSource----------------"+dataSource.getUsername()+"     "+dataSource.getPassword()+"     "+dataSource.getUrl());
        return dataSource.initializeDataSourceBuilder().build();
    }

    class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DATASOURCE_TAG;
        }
    }

}

4. 回滚已发布配置

如果发现已发布的配置有问题,可以通过点击『回滚』按钮来将客户端读取到的配置回滚到上一个发布版本。

这里的回滚机制类似于发布系统,发布系统中的回滚操作是将部署到机器上的安装包回滚到上一个部署的版本,但代码仓库中的代码是不会回滚的,从而开发可以在修复代码后重新发布。

Apollo 中的回滚也是类似的机制,点击回滚后是将发布到客户端的配置回滚到上一个已发布版本,也就是说客户端读取到的配置会恢复到上一个版本,但页面上编辑状态的配置是不会回滚的,从而开发可以在修复配置后重新发布。

5. 测试结果

以下为自己测试的一些结论:

① 在 apollo 配置中心修改配置信息后,点击发布数据会同步到 Java 客户端本地缓存文件。

② 如果Java客户端(Java项目)配置文件中配置了apollo服务器地址,则优先从服务器获取配置项,如无法正常连接服务器(服务器地址错误或其他原因),从本地缓存文件中获取配置项(如果本地的配置文件二次修改并且没有重启项则无效);

③ 如果Java客户端配置文件没有配置apollo服务器地址,则从本地的 /opt/settings/server.properties 中查看本台服务器对应的 apollo 服务器地址并连接该服务器获取配置信息;如无法连接,读取本地缓存配置文件。

修改Java客户端本地缓存文件, apollo 配置中心是不可知的。

④ a. apollo配置中心是否同步配置信息到Java客户端缓存文件,不是和本地的缓存文件内容做比较,而是和自己服务器的上一版本内容做比较。如果内容发生修改(修改配置信息并点击发布),则修改自己服务器的内容,并把新的同步到Java客户端本地缓存文件(把旧的内容全部删掉,把新的写进去)。

    b. 如果apollo配置中心没有修改配置信息以及发布,Java客户端本地缓存文件做怎样的修改apollo服务器都是不可知的,不会再同步配置信息。

⑤ 每重启一次Java项目,apollo配置中心会自动同步配置信息到Java客户端本地缓存文件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Apollo配置中心 的相关文章

  • Apollo代码学习(二)—车辆运动学模型

    Apollo代码学习 车辆运动学模型 前言 车辆模型 单车模型 Bicycle Model 车辆运动学模型 阿克曼转向几何 Ackerman turning geometry 小结 Apollo 阿波罗 是一个开放的 完整的 安全的自动驾驶
  • springboot2.0从apollo读取配置

    参考 https github com nobodyiam apollo build scripts 本篇文章包括两点 Windows环境下apollo单机搭建 springboot2 0从apollo读取配置 一 windows环境下ao
  • ABTest灰度发布

    ABtest一个总的目的和意图是 判断哪种种UI或rerank策略更优 通过事实的依据 CTR或下单率 判断哪种策略更符合用户的习惯和需求 我们经常会面临多个设计方案的选择 比如app或pc端某个界面的某个按钮是用红色还是用蓝色 是放左边还
  • bazel的使用

    bazel的使用 bazel是google开源的构建工具 可以支持多种语言的构建 这里来尝试一下如何在C 项目中使用bazel构建 安装就不介绍了 在官网很详细 输入bazel help Usage bazel
  • 如何在 apollo 客户端中将 Mutations 链接在一起

    我的状态中存储了一堆信息 我需要使用突变将其传递到我的 graphQL 服务器 但我需要在调用下一个突变之前使用每个突变的结果 因为我需要 在我的数据库中创建一个新对象 使用为该对象生成的 id 创建另一个对象 修改原对象 存储第二个对象生
  • 具有动态键的对象的 Apollo/GraphQL 字段类型

    假设我的 graphql 服务器想要以 JSON 形式获取以下数据 其中person3 and person5是一些 id persons person3 id person3 name Mike person5 id person5 na
  • 如何正确输入 Apollo 客户端 defaultOptions?

    我正在像这样设置 Apollo 客户端 const defaultOptions watchQuery fetchPolicy cache and network errorPolicy ignore query fetchPolicy c
  • 在节点中使用 apollo -(无反应)

    我想使用 apollo 进行一些批量更新 我需要从节点启动批处理脚本 例如node myscript js 我不知道该怎么做 是否有一个简单的示例可以执行入门中描述的操作 https www apollographql com docs r
  • Apollo GraphQL - 将 .graphql 架构导入为 typeDefs

    使用 graphql yoga 您可以通过执行以下操作简单地导入模式 typeDefs src schema graphql apollo server express 是否有类似的方法 如果没有 如何从外部导入 typeDefs grap
  • Apollo 的 MockedProvider 不为 withApollo 中包装的组件提供客户端

    在测试 withApollo 中包装的组件时 我使用 Apollo 文档中指定的 Apollo 的 MockedProvider 但是当酶尝试渲染该组件时 渲染器找不到clientMockedProvider 应该已经提供了 我有一个使用的
  • 不使用 refetchQueries 更新 Apollo GraphQL 缓存?

    我有一个使用 Apollo 客户端 版本 1 x 访问 GraphQL 服务器的 React 应用程序 有一个如下所示的服务器架构 type Query currentShop Shop type Shop id ID name Strin
  • Apollo-客户端自签名证书

    ApolloClient 有没有办法接受来自具有自签名证书的服务器的请求 import ApolloClient from apollo boost const client new ApolloClient uri https windo
  • 最大 MQTT 连接数

    我需要创建一个服务器场 可以处理 5 100 万个连接 5 00000 个主题 每个客户端一个 每秒处理 300k 消息 我尝试了解各种消息代理的功能 因此我目前使用两个 RHEL EC2 实例 r3 4xlarge 来获取大量可用资源 所
  • Apollo Server - 关于缓存/数据源选项的混淆

    文档 https www apollographql com docs apollo server features data sources html Using Memcached Redis as a cache storage ba
  • Apollo:数据/突变道具未传递给组件

    我有以下带有查询和突变的组件 但我的组件没有接收数据和突变道具 我的代码中是否做错或遗漏了什么 虽然查询确实被执行 但它只是没有被传递下去 this props mutate 以及 this props data 未定义 class Res
  • React-Apollo Mutation 返回空响应

    I am using AWS Appsync where I want to get a response from a successfully executed mutation When I try my setup in the A
  • React 自定义挂钩内的 Apollo GraphQL 查询

    我正在尝试列出 Rick Morty API 中的所有角色 我编写了以下钩子以在我的组件中使用 该组件将呈现结果 当我对值进行硬编码时 例如 page 1 filter name Rick 查询运行得很好 如果我尝试使用变量 它会返回错误
  • 如何使用 Apollo 后端在 TypeScript Angular 应用程序中输入部分类型?

    编辑 我正在寻找来自 Graphql Angular 社区的权威和来源答案 以提供最佳实践示例 例如 我们在 TypeScript 中定义了一个 Person 类型 interface Person firstName string las
  • 生产中未使用快速会话设置 Cookie

    我的应用程序分为客户端和服务器 客户端是托管在 Now sh 上的前端 Nextjs 应用程序 服务器是使用 Express 创建并托管在 Heroku 上的后端 因此域是 client app now sh 和 server app he
  • Apollo 服务器,Graphql - 必须提供查询字符串

    我不确定我在这里做错了什么 我现在已经被困了一段时间 让我的突变在无服务器设置中与我的 apollo server lambda 一起运行 当我尝试运行这样的查询时 我的查询工作正常 mutation signIn username Som

随机推荐

  • VMware虚拟机Ubuntu22.04忽然不能上网

    问题描述 原本正常使用的虚拟机Ubuntu22 04忽然之间不能正常上网了 xff0c 右上角的网络连接标志也不见了 尝试删除网络适配器 xff0c 并重新添加网络适配器 不能解决 尝试windows下配置网络 原来正常上网 xff0c w
  • GIT 基于TAG拉取分支

    git 基于tag拉branch 获得最新 span class token function git span origin fetch 从tag创建新的分支 span class token function git span bran
  • 栈的作用

    栈 栈 xff08 stack xff09 又名堆栈 xff0c 它是一种运算受限的线性表 其限制是仅允许在表的一端进行插入和删除运算 这一端被称为栈顶 xff0c 相对地 xff0c 把另一端称为栈底 向一个栈插入新元素又称作进栈 入栈或
  • C++中vector的用法详解

    vector 向量 C 43 43 中的一种数据结构 确切的说是一个类 它相当于一个动态的数组 当程序员无法知道自己需要的数组的规模多大时 用其来解决问题可以达到最大节约空间的目的 用法 1 文件包含 首先在程序开头处加上 include
  • ImportError: libQtGui.so.4: cannot open shared object file: No such file or directory

    报错 xff1a File home sx125 anaconda3 envs pytorch lib python3 7 site packages cv2 init py line 3 in from cv2 import Import
  • 批量更改YOLO标签类别

    原本的标签的classes txt文件中person类别是1 即第二行才是person类 xff0c 而后来找到的数据集大且标注好了 xff0c 好巧不巧person类别是0 故要将labels文件的类别都改成0 要自己先创建好空的文件夹存
  • 【CMake】编译和链接静态库和动态库

    项目结构工作原理 配置项目编译库 项目结构 span class token builtin class name span include myClass h src CMakeLists txt myClass cpp CMakeLis
  • 字符串比较大小

    1 规则 1 如果 字符串1的第n位的ASCII码值 等于 字符串2的第n位的ASCII码值 则 继续比较下一位 2 如果 字符串1的第n位的ASCII码值 大于 字符串2的第n位的ASCII码值 则 输出结果 1 表示字符串1 gt 字符
  • 将本地jar添加到Maven本地仓库

    在Maven项目中 xff0c 如果需要引入自己的jar包 xff0c 需要将jar添加到本地Maven仓库 方法一 xff1a 假设将包htmlparser jar放入了项目下的lib目录中 xff1a gt project lib ht
  • UART的奇偶校验

    1 奇校验 当数据位中 1 的个数为奇数时 xff0c 校验位为 0 xff0c 否则为 1 2 偶校验 当数据位中 1 的个数为偶数时 xff0c 校验位为 0 xff0c 否则为 1
  • windows 关闭占用端口的进程

    1 netstat ano findstr yourPortNumber 2 taskkill PID typeyourPIDhere F
  • Linux TCP server/client例程

    1 服务器端 span class token macro property span class token directive hash span span class token directive keyword include s
  • Nvidia Jetson 平台 DeepStream-6.0.1 部署 YoloV5-6.0 实现目标检测

    项目介绍 xff1a 在 Jetson 平台上利用 DeepStream 处理多路视频源 xff0c 并实现自己训练的 YoloV5 模型的部署 文章目录 前言1 YoloV5 模型训练自己的数据集1 1 建立自己的数据集1 1 1 开始之
  • 软路由保姆级入门教程 一篇看懂软路由

    前言 amp nbsp amp nbsp 玩张大妈也一年多了 xff0c 软路由改装 刷机文章写了不少 xff0c 很早就打算写篇软路由入门文章 xff0c 但是一直没落实 xff0c 原因有二 xff1a 圈子里大佬众多 xff0c 基础
  • CMake入门02-CMake中的静态库

    CMake中的静态库 静态库 文件树 CMakeLists txt include static Hello h src Hello cpp main cpp 1 1 Hello h 声明了Hello类 xff0c Hello的方法是pri
  • C++:struct与class的区别

    xff08 1 xff09 C语言中struct与class的区别 xff1a a struct只作为一种复杂数据类型定义的结构体 xff0c 不能用于面向对象编程 xff1b b C语言没有class关键字 xff08 2 xff09 C
  • Rplidar A1使用并改为ROS中3D点云输出(PointCloud2)

    这里写自定义目录标题 Rplidar A1使用并改为ROS中3D点云输出Rplidar安装测试修改后完整代码测试 Rplidar A1使用并改为ROS中3D点云输出 3D激光雷达价格不菲 xff0c 在研究过程中 xff0c 可以尝试将2D
  • Spring/SpringBoot常用注解总结!

    以下内容皆为转载 xff0c 转载地址 xff1a 接近8000字的Spring SpringBoot常用注解总结 xff01 安排 xff01 1 64 SpringBootApplication 这里先单独拎出 64 SpringBoo
  • MobaXterm 复制粘贴快捷键

    1 复制 不用设置 xff0c MobaXTerm 里面选取内容就已经复制了 xff0c 如图 xff0c 白色的内容就已经成功复制了哈哈哈哈 xff0c 真方便 注 xff1a 如果不行 xff0c 看看是否是这里没有勾上 xff08 在
  • Apollo配置中心

    1 Apollo 介绍 Apollo xff08 阿波罗 xff09 是携程框架部门研发的分布式配置中心 xff0c 能够集中化管理应用不同环境 不同集群的配置 xff0c 配置修改后能够实时推送到应用端 xff0c 并且具备规范的权限 流