零基础深入学习携程Apollo配置中心


1:Apollo配置中心简介

Apollo(阿波罗)是携程框架部门研发的配置管理平台,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性。服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。
为什么我们用了百度的disconf、spring-cloud-config携程还要另辟蹊径,研究出一套独有的Apollo呢?下面让我们通过开源配置中心对比矩阵一张图来进行解释这个问题:
开源配置中心对比矩阵.jpg
从性能对比图中我们不难看出,从功能特性、技术路线、可用性、易用性来说比其它开源配置中心是非常出色的(尤其是里面的灰度规则、namespace空间管理非常不错)。好,我们先从搭建开始讲起。

2:注册中心阿波罗Apollo搭建

Apollo开源官网
Wiki ☞ 一切的集成方式和使用方法都在这里
Issues ☞ 如果期间有任何问题,请通过这里查找大部分解决方法
说明:我这里搭建的是1.3的服务版本,1.3.1在是目前最新的版本,相对于之前的版本增加了yml、yaml等多种配置文件的支持。
下载Releases版本
下载三个已经打好的安装包:
apollo-adminservice-1.3.0-github.zip
apollo-configservice-1.3.0-github.zip
apollo-portal-1.3.0-github.zip
2.1、基础环境:
JDK:1.8.0_161
Maven:3.5.2
MySQL:5.7.18
apollo:1.3.1
2.2 导入数据库文件 登录MySQL命令行,然后执行以下两个sql文件
界面管理
配置中心
2.3、打包
分别将三个安装包进行解压

unzip apollo-adminservice-1.3.0-github.zip -d ~/apollo/apollo-adminservice
unzip apollo-configservice-1.3.0-github.zip -d ~/apollo/apollo-configservice
unzip apollo-portal-1.3.0-github.zip -d ~/apollo/apollo-portal

2.4、修改配置
分别进入apollo-adminservice、apollo-configservice的config进行配置application-github.properties 数据库配置,指向ApolloPortalDB库
进入apollo-portal的config目录编辑application-github.properties,将数据库指向ApolloPortalDB,并且编辑apollo-env.properties 将config meta server进行配置在里面
分别进行adminservice、configservice、apollo-portal等安装包的scripts目录,进行修改startup.sh脚本文件。
主要修改LOG_DIR(服务日志存储路径)、SERVER_PORT(服务启动端口,如果需要的话)以及jvm启动参数,
2.5、启动Apollo
apollo-configservice:进入scripts文件夹
执行脚本启动服务
./startup.sh
apollo-adminservice:进入scripts文件夹
切换到目录 apollo-adminservice/scripts
执行脚本启动服务
./startup.sh
apollo-portal:进入scripts文件夹
执行脚本启动服务
./startup.sh
2.6、访问Apollo配置中心页面
++http://localhost:8070++ ,默认用户名/密码参考☞ Portal 实现用户登录功能
apollo-admin-console-page.png

3:Apollo多环境搭建

在搭建完成Apollo之后我们发现,默认就存在一个环境(默认为DEV),那么我们在实际项目使用过程中会同时存在多环境情况.(即:local/dev/test/prod…..)
阿波罗多注册环境配置搭建
3.1、多环境命名注意点
apollo目前支持的环境列表名称有哪些(切记环境名字不能瞎写,否则会找不到的,小张就在这里栽跟头了,随意写了TEST,结果portal启动报错)?
详见apollo源码得知在:com.ctrip.framework.apollo.core.enums.Env枚举类
public enum Env{
LOCAL, DEV, BETA, FWS, FAT, UAT, LPT, PRO, TOOLS, UNKNOWN;

}
LOCAL - Local Development environment
DEV - Development environment
FWS - Feature Web Service Test environment
FAT - Feature Acceptance Test environment
LPT - Load and Performance Test environment
UAT - User Acceptance Test environment
PRO - Production environment
apollo-Multi-environment-naming.png
比如我需要增加一个FAT环境,那么需要新增一个config server、admin server、以及新的ApolloConfigDB库,其中config server、admin server的config下对应的数据库配置都指向新的ApolloConfigDB。
3.2、需要修改的表字段信息如下:
3.2.1、ApolloPortalDB库的ServerConfig表:
apolloPortalDB-serviceConfig.png
针对不同环境的配置文件,在Apollo中创建不同的namespace时可以指定不同角色的用户来进行管理,方便不同角色的用户进行维护不同环境的配置。

4:Apollo配置中心架构设计

搭建完成之后我们一起来熟悉下Apollo配置中心的一个架构设计。
4.1、基础模型
如下即是Apollo的基础模型:
用户在配置中心对配置进行修改并发布
配置中心通知Apollo客户端有配置更新
Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用
apollo-basic-architecture.png
4.2、架构模块
apollo-overall-architecture.png
上图简要描述了Apollo的总体设计,我们可以从下往上看:

  • Config Service提供配置的读取、推送等功能,服务对象是Apollo客户端
  • Admin Service提供配置的修改、发布等功能,服务对象是Apollo Portal(管理界面)
  • Config Service和Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳
  • 在Eureka之上我们架了一层Meta Server用于封装Eureka的服务发现接口
  • Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试
  • Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重试
  • 为了简化部署,我们实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中

4.3、Why Eureka
为什么我们采用Eureka作为服务注册中心,而不是使用传统的zk、etcd呢?大致总结了一下,有以下几方面的原因:

  • 它提供了完整的Service Registry和Service Discovery实现
    首先是提供了完整的实现,并且也经受住了Netflix自己的生产环境考验,相对使用起来会比较省心。
  • 和Spring Cloud无缝集成
    我们的项目本身就使用了Spring Cloud和Spring Boot,同时Spring Cloud还有一套非常完善的开源代码来整合Eureka,所以使用起来非常方便。
    另外,Eureka还支持在我们应用自身的容器中启动,也就是说我们的应用启动完之后,既充当了Eureka的角色,同时也是服务的提供者。这样就极大的提高了服务的可用性。
    这一点是我们选择Eureka而不是zk、etcd等的主要原因,为了提高配置中心的可用性和降低部署复杂度,我们需要尽可能地减少外部依赖。
  • Open Source
    最后一点是开源,由于代码是开源的,所以非常便于我们了解它的实现原理和排查问题。

4.4 各模块概要介绍
4.4.1 Config Service

  • 提供配置获取接口
  • 提供配置更新推送接口(基于Http long polling)
    服务端使用Spring DeferredResult实现异步化,从而大大增加长连接数量
    目前使用的tomcat embed默认配置是最多10000个连接(可以调整),使用了4C8G的虚拟机实测可以支撑10000个连接,所以满足需求(一个应用实例只会发起一个长连接)。
  • 接口服务对象为Apollo客户端

4.4.2 Admin Service

  • 提供配置管理接口
  • 提供配置修改、发布等接口
  • 接口服务对象为Portal

4.4.3 Meta Server

  • Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port)
  • Client通过域名访问Meta Server获取Config Service服务列表(IP+Port)
  • Meta Server从Eureka获取Config Service和Admin Service的服务信息,相当于是一个Eureka Client
  • 增设一个Meta Server的角色主要是为了封装服务发现的细节,对Portal和Client而言,永远通过一个Http接口获取Admin Service和Config Service的服务信息,而不需要关心背后实际的服务注册和发现组件
  • Meta Server只是一个逻辑角色,在部署时和Config Service是在一个JVM进程中的,所以IP、端口和Config Service一致

4.4.4 Eureka

  • 基于EurekaSpring Cloud Netflix提供服务注册和发现
  • Config Service和Admin Service会向Eureka注册服务,并保持心跳
  • 为了简单起见,目前Eureka在部署时和Config Service是在一个JVM进程中的(通过Spring Cloud Netflix)

4.4.5 Portal

  • 提供Web界面供用户管理配置
  • 通过Meta Server获取Admin Service服务列表(IP+Port),通过IP+Port访问服务
  • 在Portal侧做load balance、错误重试

4.4.6 Client

  • Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能
  • 通过Meta Server获取Config Service服务列表(IP+Port),通过IP+Port访问服务
  • 在Client侧做load balance、错误重试

4.5 服务端设计
4.5.1 发送ReleaseMessage的实现方式
Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见
ReleaseMessageScanner
简要描述了NotificationControllerV2是如何得知有配置发布的,那NotificationControllerV2在得知有配置发布后是如何通知到客户端的呢?
实现方式如下:

  • 客户端会发起一个Http请求到Config Service的notifications/v2接口,也就是NotificationControllerV2,参见RemoteConfigLongPollService
  • NotificationControllerV2不会立即返回结果,而是通过Spring DeferredResult把请求挂起
  • 如果在60秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端
  • 如果有该客户端关心的配置发布,NotificationControllerV2会调用DeferredResult的setResult方法,传入有配置变化的namespace信息,同时该请求会立即返回。客户端从返回的结果中获取到配置变化的namespace后,会立即请求Config Service获取该namespace的最新配置。
    apollo-client-architecture.png
    上图简要描述了Apollo客户端的实现原理:
  1. 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
  2. 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
  • 这是一个fallback机制,为了防止推送机制失效导致配置不更新
  • 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
  • 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
  1. 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
  2. 客户端会把从服务端获取到的配置在本地文件系统缓存一份
  • 在遇到服务不可用或网络不通的时候,依然能从本地恢复配置
  1. 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
    更详细资料详见

    5:微服务中Apollo java客户端的搭建使用

  2. 1、首先在微服务maven的pom项目中增加apollo的client 依赖
    <dependency>
     <groupId>com.ctrip.framework.apollo</groupId>
     <artifactId>apollo-client</artifactId>
     <version>1.1.0</version>
    </dependency>
  3. 2、在application启动类中指定@EnableApolloConfig配置项
  4. 3、启动应用中需要指定-Denv=dev参数
  5. 4、在yml中需要增加config server的服务地址配置以及serviceid配置项
    app:
    id: SampleApp
    apollo:
    meta: http://34.85.49.138:8080
    bootstrap:
      enabled: true
  6. 5、注意事项
    在多网卡机器的环境中,需要指定要注册到configServer的ip,避免内网ip或者受限制的网络ip注册到meta server中去,导致客户端获取不到元数据信息
    可以在apollo-configservice 或者apollo-adminservice 的startup.sh中指定eureka 实例化注册的ip地址。参数信息如: -Deureka.instance.ip-address=${指定的IP} 详见官网资料
  7. 6、使用@ApolloConfigChangeListener监听类进行监听配置项的改变
    @ApolloConfigChangeListener
    private void someOnChange(ConfigChangeEvent changeEvent) {
      //update injected value of batch if it is changed in Apollo
      if (changeEvent.isChanged("timeout")) {
        System.out.println(config.getIntProperty("timeout", 0));
      }
    }
  8. 7、使用简单样例
    Java Config使用方式
  9. 7.1、新建配置类JavaConfigBean如下:
/**
 * Java Config方式
 *
 * @author zyf
 * @create 2020-05-12 15:00
 **/
@Configuration
public class JavaConfigBean {
  @Value("${timeout:20}")
  private int timeout;


  public int getTimeout() {
    return timeout;
  }
}

5.7.2、新增访问端点

//1.Java Config方式
 @Autowired
 JavaConfigBean javaConfigBean;


 @RequestMapping("/index1")
 public String hello1(){
   return javaConfigBean.getTimeout()+"";
 }

Spring Boot提供了@ConfigurationProperties把配置注入到bean对象中。Apollo也支持这种方式,下面的例子会把redis.cache.expireSeconds和redis.cache.commandTimeout分别注入 到SampleRedisConfig的expireSeconds和commandTimeout字段中。
5.8.1、新增配置类SampleRedisConfig如下:

/**
 * ConfigurationProperties使用方式
 *
 * @author zyf
 * @create 2020-05-12 15:00
 **/
@Configuration
@ConfigurationProperties(prefix = "redis.cache")
public class SampleRedisConfig {
  private int expireSeconds;
  private int commandTimeout;


  public void setExpireSeconds(int expireSeconds) {
    this.expireSeconds = expireSeconds;
  }


  public void setCommandTimeout(int commandTimeout) {
    this.commandTimeout = commandTimeout;
  }


  public int getExpireSeconds() {
    return expireSeconds;
  }


  public int getCommandTimeout() {
    return commandTimeout;
  }
}
新增访问端点
//2. ConfigurationProperties使用方式
  @Autowired
  SampleRedisConfig sampleRedisConfig;


  @RequestMapping("/index2")
  public String hello2(){
    return sampleRedisConfig.getCommandTimeout()+"--"+sampleRedisConfig.getExpireSeconds();
  }
新增以下代码
@ApolloConfigChangeListener
  private void someOnChange(ConfigChangeEvent changeEvent) {
    //update injected value of batch if it is changed in Apollo
    if (changeEvent.isChanged("timeout")) {
      System.out.println(config.getIntProperty("timeout", 0));
    }
  }
@ApolloJsonValue的使用
新增User如下:
/**
 * 用户
 *
 * @author zyf
 * @create 2020-05-12 15:00
 **/
public class User {
  private String username;
  private String password;


  public String getUsername() {
    return username;
  }


  public void setUsername(String username) {
    this.username = username;
  }


  public String getPassword() {
    return password;
  }


  public void setPassword(String password) {
    this.password = password;
  }
}
服务端新增配置
jsonBeanProperty=[ { "username": "john", "password": "1234" }, { "username": "simon", "password": "222132" } ]
客户端获取配置
//4. @ApolloJsonValue使用
@ApolloJsonValue("${jsonBeanProperty:[]}")
private List<User> anotherJsonBeans;


@RequestMapping("/index4")
public void hello4(){
  anotherJsonBeans.forEach(item -> {
    System.err.println(item.getUsername()+"--"+item.getPassword());
  });
}

5.8.2、多配置文件

package com.xes.chn.apollo.service.apollo.impl;

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 com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Set;

@Service
@EnableApolloConfig({"chn.application.properties", "beibo.yml"})
//@EnableApolloConfig("application.properties")
public class LoggerConfigurationImpl {
    private static final Logger logger = LoggerFactory.getLogger(LoggerConfigurationImpl.class);


    @ApolloConfig("chn.application.properties")
    private Config commonProperties;
    @ApolloConfig("beibo.yml")
    private Config beiboYml;
    @Value("${invokingServerPath.beibo.stuChangeUrl}")
    private String stuChangeUrl;
    //
    @PostConstruct
    public void init() {
//        System.out.println(gateway);
//        System.out.println(config.getPropertyNames());
//        Config config = ConfigService.getConfig("chn-beibo.yml"); //config instance is singleton for each namespace and is never null
//        String someKey = "chn-beibo";
//        String someDefaultValue = "1234567a";
//        String value = config.getProperty(someKey, someDefaultValue);
//        System.out.println(gateway);
    }

    @ApolloConfigChangeListener({"chn.application.properties", "beibo.yml"})
    private void someOnChange(ConfigChangeEvent changeEvent) {
        //update injected value of batch if it is changed in Apollo
        if (changeEvent.isChanged("timeout")) {
//            System.out.println(config.getIntProperty("timeout", 0));
        }
    }

//    @Scheduled(fixedRate = 1000)
    public void reportCurrentTime() {
        System.out.println(stuChangeUrl);
        Set<String> propertyNames=commonProperties.getPropertyNames();
        String stuChangeUrL=beiboYml.getProperty("invokingServerPath.beibo.stuChangeUrl","");
        for (String propertyName : propertyNames) {
            if(stuChangeUrL.contains(propertyName)){
                logger.info("stuChangeUrL:{}", stuChangeUrL.replace("${" + propertyName + "}",commonProperties.getProperty(propertyName,"")));
            }
        }
    }

}

6:带缓存与不带缓存Http接口从Apollo读取配置

6.1 通过带缓存的Http接口从Apollo读取配置
该接口会从缓存中获取配置,适合频率较高的配置拉取请求,如简单的每30秒轮询一次配置。

由于缓存最多会有一秒的延时,所以如果需要配合配置推送通知实现实时更新配置的话,请参考通过不带缓存的Http接口从Apollo读取配置
6.1.1 Http接口说明
URL: {config_server_url}/configfiles/json/{appId}/{clusterName}/{namespaceName}?ip={clientIp}
Method: GET
参数说明:

参数名 是否必须 参数值 备注
config_server_url Apollo配置服务的地址
appId 应用的appId
namespaceName 如果没有新建过Namespace的话,传入application即可。 如果创建了Namespace,并且需要使用该Namespace的配置,则传入对应的Namespace名字。需要注意的是对于properties类型的namespace,只需要传入namespace的名字即可,如application。对于其它类型的namespace,需要传入namespace的名字加上后缀名,如datasources.json
ip 应用部署的机器ip 这个参数是可选的,用来实现灰度发布。 如果不想传这个参数,请注意URL中从?号开始的query parameters整个都不要出现。

6.2 Http接口返回格式
该Http接口返回的是JSON格式、UTF-8编码,包含了对应namespace中所有的配置项。
返回内容Sample如下:

{
    "portal.elastic.document.type":"biz",
    "portal.elastic.cluster.name":"hermes-es-fws"
}
通过
{config_server_url}/configfiles/{appId}/{clusterName}/{namespaceName}?ip={clientIp}可以获取到properties形式的配置

6.3 测试
由于是Http接口,所以在URL组装OK之后,直接通过浏览器、或者相关的http接口测试工具访问即可。
6.4 通过不带缓存的Http接口从Apollo读取配置
该接口会直接从数据库中获取配置,可以配合配置推送通知实现实时更新配置。
6.5 Http接口说明
URL: {config_server_url}/configs/{appId}/{clusterName}/{namespaceName}?releaseKey={releaseKey}&ip={clientIp}
Method: GET
参数说明:

参数名 是否必须 参数值 备注
config_server_url Apollo配置服务的地址
appId 应用的appId
clusterName 集群名 一般情况下传入 default 即可。 如果希望配置按集群划分,可以参考集群独立配置说明做相关配置,然后在这里填入对应的集群名。
namespaceName Namespace的名字 如果没有新建过Namespace的话,传入application即可。 如果创建了Namespace,并且需要使用该Namespace的配置,则传入对应的Namespace名字。需要注意的是对于properties类型的namespace,只需要传入namespace的名字即可,如application。对于其它类型的namespace,需要传入namespace的名字加上后缀名,如datasources.json
releaseKey 上一次的releaseKey 将上一次返回对象中的releaseKey传入即可,用来给服务端比较版本,如果版本比下来没有变化,则服务端直接返回304以节省流量和运算
ip 应用部署的机器ip 这个参数是可选的,用来实现灰度发布。

6.5 Http接口返回格式
该Http接口返回的是JSON格式、UTF-8编码。

如果配置没有变化(传入的releaseKey和服务端的相等),则返回HttpStatus 304,response body为空。
如果配置有变化,则会返回HttpStatus 200,response body为对应namespace的meta信息以及其中所有的配置项。
返回内容Sample如下:

{
  "appId": "100004458",
  "cluster": "default",
  "namespaceName": "application",
  "configurations": {
    "portal.elastic.document.type":"biz",
    "portal.elastic.cluster.name":"hermes-es-fws"
  },
  "releaseKey": "20170430092936-dee2d58e74515ff3"
}

6.6 测试
由于是Http接口,所以在URL组装OK之后,直接通过浏览器、或者相关的http接口测试工具访问即可。
6.7 应用感知配置更新
Apollo提供了基于Http long polling的配置更新推送通知,第三方客户端可以看自己实际的需求决定是否需要使用这个功能。
如果对配置更新时间不是那么敏感的话,可以通过定时刷新来感知配置更新,刷新频率可以视应用自身情况来定,建议在30秒以上。
如果需要做到实时感知配置更新(1秒)的话,可以参考下面的文档实现配置更新推送的功能。
6.8 配置更新推送实现思路
这里建议大家可以参考Apollo的Java实现:RemoteConfigLongPollService.java,代码量200多行,总体上还是比较简单的。
6.8.1 初始化
首先需要确定哪些namespace需要配置更新推送,Apollo的实现方式是程序第一次获取某个namespace的配置时就会来注册一下,我们就知道有哪些namespace需要配置更新推送了。
初始化后的结果就是得到一个notifications的Map,内容是namespaceName -> notificationId(初始值为-1)。
运行过程中如果发现有新的namespace需要配置更新推送,直接塞到notifications这个Map里面即可。
6.8.2 请求服务
有了notifications这个Map之后,就可以请求服务了。这里先描述一下请求服务的逻辑,具体的URL参数和说明请参见后面的接口说明。

  1. 请求远端服务,带上自己的应用信息以及notifications信息
  2. 服务端针对传过来的每一个namespace和对应的notificationId,检查notificationId是否是最新的
  3. 如果都是最新的,则保持住请求60秒,如果60秒内没有配置变化,则返回HttpStatus 304。如果60秒内有配置变化,则返回对应namespace的最新notificationId, HttpStatus 200。
  4. 如果传过来的notifications信息中发现有notificationId比服务端老,则直接返回对应namespace的最新notificationId, HttpStatus 200。
  5. 客户端拿到服务端返回后,判断返回的HttpStatus
  6. 如果返回的HttpStatus是304,说明配置没有变化,重新执行第1步
  7. 如果返回的HttpStauts是200,说明配置有变化,针对变化的namespace重新去服务端拉取配置,参见1.3 通过不带缓存的Http接口从Apollo读取配置。同时更新notifications map中的notificationId。重新执行第1步。

6.9 Http接口说明
URL: {config_server_url}/notifications/v2?appId={appId}&cluster={clusterName}&notifications={notifications}
Method: GET
参数说明:

参数名 是否必须 参数值 备注
notifications notifications信息 传入本地的notifications信息,注意这里需要以array形式转为json传入,如:[{“namespaceName”: “application”, “notificationId”: 100}, {“namespaceName”: “FX.apollo”, “notificationId”: 200}]。需要注意的是对于properties类型的namespace,只需要传入namespace的名字即可,如application。对于其它类型的namespace,需要传入namespace的名字加上后缀名,如datasources.json
config_server_url Apollo配置服务的地址
clusterName 集群名 一般情况下传入 default 即可。 如果希望配置按集群划分,可以参考集群独立配置说明做相关配置,然后在这里填入对应的集群名。
appId 应用的appId

注1:由于服务端会hold住请求60秒,所以请确保客户端访问服务端的超时时间要大于60秒。
注2:别忘了对参数进行url encode
6.9.1 Http接口返回格式
该Http接口返回的是JSON格式、UTF-8编码,包含了有变化的namespace和最新的notificationId。
返回内容Sample如下:

[
  {
    "namespaceName": "application",
    "notificationId": 101
  }
]

6.9.2 测试
6.9.3 错误码说明
正常情况下,接口返回的Http状态码是200,下面列举了Apollo会返回的非200错误码说明。

序号 错误状态码 说明
1 400 - Bad Request 客户端传入参数的错误,如必选参数没有传入等,客户端需要根据提示信息检查对应的参数是否正确。
2 404 - Not Found 接口要访问的资源不存在,一般是URL或URL的参数错误,或者是对应的namespace还没有发布过配置。
3 405 - Method Not Allowed 接口访问的Method不正确,比如应该使用GET的接口使用了POST访问等,客户端需要检查接口访问方式是否正确。
4 500 - Internal Server Error 其它类型的错误默认都会返回500,对这类错误如果应用无法根据提示信息找到原因的话,可以尝试查看服务端日志来排查问题。

7.1 什么是yml占位符?举个例子如下:
我有2分配置文件,一份application.yml,为私有配置文件
该配置有个属性:
server:
port: ${server.apollo-test-port}
另外有个公有配置文件
apollo-test.yml,该配置有个属性,
server:
apollo-test-port: 10065

其中私有配置文件,使用到了占位符, 请问下,这种在阿波罗有实现吗?如果有,需要怎么做? 详见参考我之前在github中提交的issues,得到了作者的答复 github issues
目前非properties格式是通过api方式获取的,ConfigService.getConfigFile,具体使用示例参照
github非properties配置文件读取样例
好了,到此就先总结这么多,后续还有更新的工作中躺过的坑或使用的问题我会实时更新到这里来,当然最主要的还是希望后续Apollo注册中心服务发挥出更出色的能力为我们的产品发挥出更大的可利用价值。


文章作者: 小张哥
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小张哥 !
评论
 上一篇
java8新特性原理以及实战应用-lambda函数式编程与日期篇 java8新特性原理以及实战应用-lambda函数式编程与日期篇
1:简介目前截止2021年9月java最新的jdk版本为17,而我们目前大部分公司现在的java项目都还在运行的是java8的版本,对于我们目前的项目而言,java8已经能够满足我们大部分工作中的应用场景。而且截止目前为止oracle公司还
下一篇 
golang日志篇-log与fmt区别 golang日志篇-log与fmt区别
最近在排查golang服务上报丢失日志的问题。发现服务中记录日志的操作大部分使用都是golang log包下的工具类进行操作的,那么log与fmt二者有什么区别?通过下面的样例来具体了解下二者的区别。 1:线程安全层面我们可以运行以下示例
  目录