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配置中心 的相关文章

  • 微服务2-nacos 配置中心

    1什么是配置中心 在微服务架构中 当系统从一个单体应用 被拆分成分布式系统上一个个服务节点后 配置文件也必须跟着迁移 分割 这样配置就分散了 不仅如此 分散中还包含着冗余 如下图 2为什么要使用配置中心 配置中心将配置从各应用中剥离出来 对
  • Apollo客户端配置获取深度解析

    Apollo客户端配置获取深度解析 Apollo 阿波罗 是携程框架部门研发的开源配置管理中心 能够集中化管理应用不同环境 不同集群的配置 配置修改后能够实时推送到应用端 并且具备规范的权限 流程治理等特性 这篇文章主要来剖析客户端获取配置
  • Apollo Cyber RT学习手册(基于Ubuntu18.04、Apollo 6.0_edu)

    本文全部内容源自赵虚左老师的 百度Apollo智能驾驶课程 如有侵权请联系 本文内容为作者手敲 禁止任何形式转载 谢谢 第一章 Cyber RT概述与环境搭建 1 1Cyber RT简介 Apollo项目源于2017年7月首次推出 标志着汽
  • Apollo平台计算框架CyberRT

    Cyber RT 框架 Cyber RT Robotic Technology 是一种基于ROS Robot Operating System 的开发框架 专门设计用于构建高性能 实时性要求较高的机器人应用程序 旨在提供一套可靠 高效 灵活
  • 如何在 apollo 客户端中将 Mutations 链接在一起

    我的状态中存储了一堆信息 我需要使用突变将其传递到我的 graphQL 服务器 但我需要在调用下一个突变之前使用每个突变的结果 因为我需要 在我的数据库中创建一个新对象 使用为该对象生成的 id 创建另一个对象 修改原对象 存储第二个对象生
  • GraphQL 突变后的 updateQueries 不适用于 Apollo 客户端

    当我发送一个createMessage我的应用程序发生突变 我想更新本地ApolloStore using updateQueries 我的设置如下 const ChatWithAllMessages graphql allMessages
  • 在生产模式下是否有禁用游乐场的选项?

    apollo v3 中是否有任何选项可以在生产模式下禁用游乐场 在 v2 中你可以通过传递来禁用它playground false到 ApolloServer 选项 但在 v3 中找不到任何选项 我正在使用 apollo server fa
  • 在节点中使用 apollo -(无反应)

    我想使用 apollo 进行一些批量更新 我需要从节点启动批处理脚本 例如node myscript js 我不知道该怎么做 是否有一个简单的示例可以执行入门中描述的操作 https www apollographql com docs r
  • 如何在 Zeit Now 上使用 GraphQL 后端部署 Next.js?

    我有一个 Next js Express Apollo GraphQL 应用程序在本地主机上运行良好 我尝试将其部署在 Zeit Now 上 Next js 部分工作正常 但 GraphQL 后端失败 因为 graphql路线返回 502
  • Apollo GraphQL - 将 .graphql 架构导入为 typeDefs

    使用 graphql yoga 您可以通过执行以下操作简单地导入模式 typeDefs src schema graphql apollo server express 是否有类似的方法 如果没有 如何从外部导入 typeDefs grap
  • Apollo Server Express:请求实体太大

    我需要在 GraphQL 突变中发布大量有效负载 如何提高 Apollo Server 的主体大小限制 我在用着apollo server express版本 2 9 3 我的代码 简化 const myGraphQLSchema new
  • 将 graphql 变量传递给 Material UI 组件

    我正在使用 React Typescript 并有一个自动完成材质 UI 组件 我正在尝试将查询建议添加到自动完成组件中 我的 graphql 查询如下所示 查询定义 import gql from graphql tag import u
  • 如何在 Jest 测试中处理 .gql 文件导入

    我正在尝试测试一个导入的组件 gql文件 当我尝试在 Jest 文件中构建组件时 收到以下错误 object anonymous function module exports require dirname filename global
  • 如何仅在用户身份验证后设置 Apollo 客户端?

    我对如何构建我的 React GraphQL Apollo 应用程序感到有点困惑 因为在用户验证 登录之前不应建立连接 目前我有这个 class App extends Component render return
  • AWS AppSync:如何通过 DynamoDB 返回有效的 JSON

    我有一个 AppSync GraphQL API 可以对 DynamoDB 进行查询并返回 JSON 字符串 但是在我的响应映射模板中 我使用内置 util parseJson 功能如所列here https docs aws amazon
  • Apollo重新获取不重新渲染组件

    我正在使用 graphql 从网络服务获取数据 我的客户端代码是这样的 import React Component from react import Platform StyleSheet Text ActivityIndicator
  • Apollo:数据/突变道具未传递给组件

    我有以下带有查询和突变的组件 但我的组件没有接收数据和突变道具 我的代码中是否做错或遗漏了什么 虽然查询确实被执行 但它只是没有被传递下去 this props mutate 以及 this props data 未定义 class Res
  • React 未捕获的不变违规:在 ApolloConsumer 的上下文中找不到“客户端”。将根组件包装在

    我有一个使用 React 应用程序react apollo 2 5 8以及我通过 NPM 安装并在应用程序中使用的自定义组件库 图书馆有react apollo 2 5 8列在对等和开发依赖项中 自从我将 React apollo 升级到
  • 如何在 Vanilla JS 中使用 Apollo Client 创建 GraphQL 订阅

    最近 Apollo Client 发布了 websocket 订阅功能 但到目前为止我只看到它通过在 componentWillMount 生命周期钩子中使用 subscribeToMore 启动查询来使用 这是一个来自的例子https d
  • 如何使用 Nuxt ServerMiddleware 和 Apollo GraphQL 处理 301 重定向

    我目前正在构建一个 Vue Nuxt 应用程序 并结合修改后的 Saleor 安装来在线销售产品 当我们从现有系统迁移时 我们需要能够处理从旧网站 URL 到新网站 URL 的 301 重定向 我修改了 API 以响应以下 GraphQL

随机推荐

  • 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 并且具备规范的权限 流