apolloJava客户端的使用
参考携程官网提供的https://github.com/ctripcorp/apollo/wiki/Java%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97
整个项目组的代码如下
项目的pom.xml代码如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itmayiedu</groupId> <artifactId>springboot-abl</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </exclusion> </exclusions> --> </dependency> <!-- apollo 携程apollo配置中心框架 --> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-core</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-conf</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <encoding>UTF-8</encoding> <outputDirectory>${project.build.directory}/ext/conf</outputDirectory> <resources> <resource> <directory>ext/conf</directory> <includes> <include>logback.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.5.201505241946</version> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>default-prepare-agent-integration</id> <goals> <goal>prepare-agent-integration</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.3</version> <configuration> <imageName>hy_uav_gateway</imageName> <dockerDirectory>src/main/docker</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> <include>ext/conf/logback.xml</include> </resource> </resources> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
这里引入携程的客户端apollo-client为最新的1.7版本
环境准备:
客户端要指定应用的appID
AppId是应用的身份信息,是从服务端获取配置的一个重要信息。
有以下几种方式设置,按照优先级从高到低分别为:
- System Property
Apollo 0.7.0+支持通过System Property传入app.id信息,如
-Dapp.id=YOUR-APP-ID
- 操作系统的System Environment
Apollo 1.4.0+支持通过操作系统的System Environment APP_ID
来传入app.id信息,如
APP_ID=YOUR-APP-ID
- Spring Boot application.properties
Apollo 1.0.0+支持通过Spring Boot的application.properties文件配置,如
app.id=YOUR-APP-ID
该配置方式不适用于多个war包部署在同一个tomcat的使用场景
- app.properties
确保classpath:/META-INF/app.properties文件存在,并且其中内容形如:
app.id=YOUR-APP-ID
文件位置参考如下:
注:app.id是用来标识应用身份的唯一id,格式为string。
这里我们采用classpath:/META-INF/app.properties下面来指定应用的appID
app.properties的内容如下,app.id的值必须和apollo上面创建的应用的AppID一致
1.2.2 Apollo Meta Server
Apollo支持应用在不同的环境有不同的配置,所以需要在运行提供给Apollo客户端当前环境的Apollo Meta Server信息。默认情况下,meta server和config service是部署在同一个JVM进程,所以meta server的地址就是config service的地址。
为了实现meta server的高可用,推荐通过SLB(Software Load Balancer)做动态负载均衡。Meta server地址也可以填入IP,如
http://1.1.1.1:8080,http://2.2.2.2:8080
,不过生产环境还是建议使用域名(走slb),因为机器扩容、缩容等都可能导致IP列表的变化。1.0.0版本开始支持以下方式配置apollo meta server信息,按照优先级从高到低分别为:
- 通过Java System Property
apollo.meta
- 可以通过Java的System Property
apollo.meta
来指定- 在Java程序启动脚本中,可以指定
-Dapollo.meta=http://config-service-url
- 如果是运行jar文件,需要注意格式是
java -Dapollo.meta=http://config-service-url -jar xxx.jar
- 也可以通过程序指定,如
System.setProperty("apollo.meta", "http://config-service-url");
- 通过Spring Boot的配置文件
- 可以在Spring Boot的
application.properties
或bootstrap.properties
中指定apollo.meta=http://config-service-url
该配置方式不适用于多个war包部署在同一个tomcat的使用场景
- 通过操作系统的System Environment
APOLLO_META
- 可以通过操作系统的System Environment
APOLLO_META
来指定- 注意key为全大写,且中间是
_
分隔- 通过
server.properties
配置文件
- 可以在
server.properties
配置文件中指定apollo.meta=http://config-service-url
- 对于Mac/Linux,文件位置为
/opt/settings/server.properties
- 对于Windows,文件位置为
C:\opt\settings\server.properties
- 通过
app.properties
配置文件
- 可以在
classpath:/META-INF/app.properties
指定apollo.meta=http://config-service-url
- 通过Java system property
${env}_meta
- 如果当前env是
dev
,那么用户可以配置-Ddev_meta=http://config-service-url
- 使用该配置方式,那么就必须要正确配置Environment,详见1.2.4.1 Environment
- 通过操作系统的System Environment
${ENV}_META
(1.2.0版本开始支持)
- 如果当前env是
dev
,那么用户可以配置操作系统的System EnvironmentDEV_META=http://config-service-url
- 注意key为全大写
- 使用该配置方式,那么就必须要正确配置Environment,详见1.2.4.1 Environment
- 通过
apollo-env.properties
文件
- 用户也可以创建一个
apollo-env.properties
,放在程序的classpath下,或者放在spring boot应用的config目录下- 使用该配置方式,那么就必须要正确配置Environment,详见1.2.4.1 Environment
- 文件内容形如:
dev.meta=http://1.1.1.1:8080 fat.meta=http://apollo.fat.xxx.com uat.meta=http://apollo.uat.xxx.com pro.meta=http://apollo.xxx.com
如果通过以上各种手段都无法获取到Meta Server地址,Apollo最终会fallback到
http://apollo.meta
作为Meta Server地址
这里我们采用第8钟方式,apollo-env.properties
文件,这里支持两种环境,一种是dev,一种是pro
1.2.2.2 跳过Apollo Meta Server服务发现
适用于apollo-client 0.11.0及以上版本
一般情况下都建议使用Apollo的Meta Server机制来实现Config Service的服务发现,从而可以实现Config Service的高可用。不过apollo-client也支持跳过Meta Server服务发现,主要用于以下场景:
- Config Service部署在公有云上,注册到Meta Server的是内网地址,本地开发环境无法直接连接
- 如果通过公网 SLB 对外暴露 Config Service的话,记得要设置 IP 白名单,避免数据泄露
- Config Service部署在docker环境中,注册到Meta Server的是docker内网地址,本地开发环境无法直接连接
- Config Service部署在kubernetes中,希望使用kubernetes自带的服务发现能力(Service)
针对以上场景,可以通过直接指定Config Service地址的方式来跳过Meta Server服务发现,按照优先级从高到低分别为:
- 通过Java System Property
apollo.configService
- 可以通过Java的System Property
apollo.configService
来指定 - 在Java程序启动脚本中,可以指定
-Dapollo.configService=http://config-service-url:port
- 如果是运行jar文件,需要注意格式是
java -Dapollo.configService=http://config-service-url:port -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 也可以通过程序指定,如
System.setProperty("apollo.configService", "http://config-service-url:port");
- 可以通过Java的System Property
- 通过操作系统的System Environment
APOLLO_CONFIGSERVICE
- 可以通过操作系统的System Environment
APOLLO_CONFIGSERVICE
来指定 - 注意key为全大写,且中间是
_
分隔
- 可以通过操作系统的System Environment
- 通过
server.properties
配置文件 - 可以在
server.properties
配置文件中指定apollo.configService=http://config-service-url:port
- 对于Mac/Linux,文件位置为
/opt/settings/server.properties
- 对于Windows,文件位置为
C:\opt\settings\server.properti
-
1.2.3.1 自定义缓存路径
1.0.0版本开始支持以下方式自定义缓存路径,按照优先级从高到低分别为:
- 通过Java System Property
apollo.cacheDir
- 可以通过Java的System Property
apollo.cacheDir
来指定 - 在Java程序启动脚本中,可以指定
-Dapollo.cacheDir=/opt/data/some-cache-dir
- 如果是运行jar文件,需要注意格式是
java -Dapollo.cacheDir=/opt/data/some-cache-dir -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 也可以通过程序指定,如
System.setProperty("apollo.cacheDir", "/opt/data/some-cache-dir");
- 可以通过Java的System Property
- 通过Spring Boot的配置文件
- 可以在Spring Boot的
application.properties
或bootstrap.properties
中指定apollo.cacheDir=/opt/data/some-cache-dir
- 可以在Spring Boot的
- 通过操作系统的System Environment
APOLLO_CACHEDIR
- 可以通过操作系统的System Environment
APOLLO_CACHEDIR
来指定 - 注意key为全大写,且中间是
_
分隔
- 可以通过操作系统的System Environment
- 通过
server.properties
配置文件- 可以在
server.properties
配置文件中指定apollo.cacheDir=/opt/data/some-cache-dir
- 对于Mac/Linux,文件位置为
/opt/settings/server.properties
- 对于Windows,文件位置为
C:\opt\settings\server.properties
- 可以在
注:本地缓存路径也可用于容灾目录,如果应用在所有config service都挂掉的情况下需要扩容,那么也可以先把配置从已有机器上的缓存路径复制到新机器上的相同缓存路径,我们来验证下缓存数据
我们通过第二种方式
通过Spring Boot的配置文件
可以在Spring Boot的application.properties或bootstrap.properties中指定apollo.cacheDir=/opt/data/some-cache-dir我们来看下文件
接下来我们来看下缓存文件
可以看到应用缓存文件的命名为:weiyuantest+default+application.properties
第一个weiyuantest是对应的应用的appID
第二个default是当前应用对于的集群名称
第三个application是当前应用的namespace
如weiyuantest+default+spring-redis.properties
第一个weiyuantest是对应的应用的appID
第二个default是当前应用对于的集群名称
第三个spring-redis是当前应用的namespace
我们打开一个配置文件看看里面的内容weiyuantest+default+spring-rocketmq.properties,里面就是key和value的键值队
#Persisted by DefaultConfig #Wed Sep 02 13:27:29 CST 2020 yushengjun=jjkfeskkjdskjf kjkjdfskj jk333333333333333333333333333337777777766666666666666666 skywalking=22222
1.2.4 可选设置
1.2.4.1 Environment
Environment可以通过以下3种方式的任意一个配置:
-
通过Java System Property
- 可以通过Java的System Property
env
来指定环境 - 在Java程序启动脚本中,可以指定
-Denv=YOUR-ENVIRONMENT
- 如果是运行jar文件,需要注意格式是
java -Denv=YOUR-ENVIRONMENT -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 注意key为全小写
- 可以通过Java的System Property
-
通过操作系统的System Environment
- 还可以通过操作系统的System Environment
ENV
来指定 - 注意key为全大写
- 还可以通过操作系统的System Environment
-
通过配置文件
- 最后一个推荐的方式是通过配置文件来指定
env=YOUR-ENVIRONMENT
- 对于Mac/Linux,文件位置为
/opt/settings/server.properties
- 对于Windows,文件位置为
C:\opt\settings\server.properties
- 最后一个推荐的方式是通过配置文件来指定
文件内容形如:
env=DEV
目前,
env
支持以下几个值(大小写不敏感):- DEV
- Development environment
- FAT
- Feature Acceptance Test environment
- UAT
- User Acceptance Test environment
- PRO
- Production environment
更多环境定义,可以参考Env.java
这里环境里面,我们在windows环境上,我们采用最后一个推荐的方式是通过配置文件来指定env=YOUR-ENVIRONMENT
这里我们设置环境变量为DEV环境,我们来看下server.properties的内容
这里设置之后,当前的应用就只能收到dev环境下面的配置,不能收到pro环境或者其他环境的配置信息了
1.2.4.2 Cluster(集群)
Apollo支持配置按照集群划分,也就是说对于一个appId和一个环境,对不同的集群可以有不同的配置。
1.0.0版本开始支持以下方式集群,按照优先级从高到低分别为:
- 通过Java System Property
apollo.cluster
- 可以通过Java的System Property
apollo.cluster
来指定 - 在Java程序启动脚本中,可以指定
-Dapollo.cluster=SomeCluster
- 如果是运行jar文件,需要注意格式是
java -Dapollo.cluster=SomeCluster -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 也可以通过程序指定,如
System.setProperty("apollo.cluster", "SomeCluster");
- 可以通过Java的System Property
- 通过Spring Boot的配置文件
- 可以在Spring Boot的
application.properties
或bootstrap.properties
中指定apollo.cluster=SomeCluster
- 可以在Spring Boot的
- 通过Java System Property
- 可以通过Java的System Property
idc
来指定环境 - 在Java程序启动脚本中,可以指定
-Didc=xxx
- 如果是运行jar文件,需要注意格式是
java -Didc=xxx -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 注意key为全小写
- 可以通过Java的System Property
- 通过操作系统的System Environment
- 还可以通过操作系统的System Environment
IDC
来指定 - 注意key为全大写
- 还可以通过操作系统的System Environment
- 通过
server.properties
配置文件- 可以在
server.properties
配置文件中指定idc=xxx
- 对于Mac/Linux,文件位置为
/opt/settings/server.properties
- 对于Windows,文件位置为
C:\opt\settings\server.properties
- 可以在
Cluster Precedence(集群顺序)
-
如果
apollo.cluster
和idc
同时指定:- 我们会首先尝试从
apollo.cluster
指定的集群加载配置 - 如果没找到任何配置,会尝试从
idc
指定的集群加载配置 - 如果还是没找到,会从默认的集群(
default
)加载
- 我们会首先尝试从
-
如果只指定了
apollo.cluster
:- 我们会首先尝试从
apollo.cluster
指定的集群加载配置 - 如果没找到,会从默认的集群(
default
)加载
- 我们会首先尝试从
-
如果只指定了
idc
:- 我们会首先尝试从
idc
指定的集群加载配置 - 如果没找到,会从默认的集群(
default
)加载
- 我们会首先尝试从
-
如果
apollo.cluster
和idc
都没有指定:- 我们会从默认的集群(
default
)加载配置
- 我们会从默认的集群(
这里我们可以在Spring Boot的application.properties或bootstrap.properties中指定apollo.cluster=SomeCluster,来设置集群的数据
这里我们指定当前应用访问的集群为jiqun1,这里需要注意的是如果jiqun1中没有对于的配置项的值,这里的配置项包括自己私有的namespace和公共的namespace,默认会访问apollo默认的default集群会从默认的集群(
default
)加载配置,因为上面我们收到的指定了集群为jiqun1,就不会再去访问默认的default集群了,如果没有指定会默认访问default集群1.2.4.3 设置内存中的配置项是否保持和页面上的顺序一致
适用于1.6.0及以上版本
默认情况下,apollo client内存中的配置存放在Properties中(底下是Hashtable),不会刻意保持和页面上看到的顺序一致,对绝大部分的场景是没有影响的。不过有些场景会强依赖配置项的顺序(如spring cloud zuul的路由规则),针对这种情况,可以开启OrderedProperties特性来使得内存中的配置顺序和页面上看到的一致。
配置方式按照优先级从高到低分别为:
- 通过Java System Property
apollo.property.order.enable
- 可以通过Java的System Property
apollo.property.order.enable
来指定 - 在Java程序启动脚本中,可以指定
-Dapollo.property.order.enable=true
- 如果是运行jar文件,需要注意格式是
java -Dapollo.property.order.enable=true -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 也可以通过程序指定,如
System.setProperty("apollo.property.order.enable", "true");
- 可以通过Java的System Property
- 通过Spring Boot的配置文件
- 可以在Spring Boot的
application.properties
或bootstrap.properties
中指定apollo.property.order.enable=true
- 可以在Spring Boot的
- 通过
app.properties
配置文件- 可以在
classpath:/META-INF/app.properties
指定apollo.property.order.enable=tru
- 可以在
我们来验证下配置项的顺序验证功能
1.2.4.4 配置访问秘钥
适用于1.6.0及以上版本
Apollo从1.6.0版本开始增加访问秘钥机制,从而只有经过身份验证的客户端才能访问敏感配置。如果应用开启了访问秘钥,客户端需要配置秘钥,否则无法获取配置。
配置方式按照优先级从高到低分别为:
- 通过Java System Property
apollo.accesskey.secret
- 可以通过Java的System Property
apollo.accesskey.secret
来指定 - 在Java程序启动脚本中,可以指定
-Dapollo.accesskey.secret=1cf998c4e2ad4704b45a98a509d15719
- 如果是运行jar文件,需要注意格式是
java -Dapollo.accesskey.secret=1cf998c4e2ad4704b45a98a509d15719 -jar xxx.jar
- 如果是运行jar文件,需要注意格式是
- 也可以通过程序指定,如
System.setProperty("apollo.accesskey.secret", "1cf998c4e2ad4704b45a98a509d15719");
- 可以通过Java的System Property
- 通过Spring Boot的配置文件
- 可以在Spring Boot的
application.properties
或bootstrap.properties
中指定apollo.accesskey.secret=1cf998c4e2ad4704b45a98a509d15719
- 可以在Spring Boot的
- 通过操作系统的System Environment
- 还可以通过操作系统的System Environment
APOLLO_ACCESSKEY_SECRET
来指定 - 注意key为全大写
- 还可以通过操作系统的System Environment
- 通过
app.properties
配置文件- 可以在
classpath:/META-INF/app.properties
指定apollo.accesskey.secret=1cf998c4e2ad4704b45a98a509d15719
- 可以在
首先我们需要在apollo的管理上设置秘钥
这里会生成对于的秘钥,接下来
通过Spring Boot的配置文件
可以在Spring Boot的application.properties或bootstrap.properties中指定apollo.accesskey.secret=1cf998c4e2ad4704b45a98a509d15719我们在客户端看看对于的application.yml中添加对于的配置项
server: port: 8001 spring: application: name: springboot-abl eureka: client: service-url: defaultZone: http://localhost:8080/eureka apollo: cacheDir: d:\cache-dir cluster: default accesskey: secret: 5b02a3e3a9ee4a72aefe3786fc02b2fe property: order: enable: true
这样客户端访问的时候就能够获得对于的配置项了,如果不携带token就会有问题
Spring Boot集成方式(推荐)
使用方式很简单,只需要在application.properties/bootstrap.properties中按照如下样例配置即可。
- 注入默认
application
namespace的配置示例
# will inject 'application' namespace in bootstrap phase apollo.bootstrap.enabled = true
- 注入非默认
application
namespace或多个namespace的配置示例
apollo.bootstrap.enabled = true # will inject 'application', 'FX.apollo' and 'application.yml' namespaces in bootstrap phase apollo.bootstrap.namespaces = application,FX.apollo,application.yml
我们来看下具体的配置server: port: 8001 spring: application: name: springboot-abl eureka: client: service-url: defaultZone: http://localhost:8080/eureka apollo: cacheDir: d:\cache-dir cluster: default bootstrap: enabled: true namespaces: application,spring-rocketmq,spring-redis accesskey: secret: 5b02a3e3b9ee4a72aefe3786fc02b2fe1 property: order: enable: true
我们来看下对于的代码
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:09:14<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ //@EnableApolloConfig({"application", "spring-rocketmq","spring-redis"}) @EnableApolloConfig @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:07:25<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @RestController public class IndexController { @Value("${yushengjun:test}") private String yushengjun; @RequestMapping("/getYushengjun") public String getYushengjun() { return yushengjun; } }
我们在配置文件中配置了 apollo.bootstrap.namespaces = application,spring-rocketmq,spring-redis,用来监听application,spring-rocketmq,spring-redis这几个namespace的值,这里一定要注意配置要生效一定要将 apollo.bootstrap设置为true
除了在application.yml中指定监听namespace的值之外,我们还可以在在启动类中使用下面的方式指定监听类
@EnableApolloConfig({"application", "spring-rocketmq","spring-redis"}) @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
在IndexController中我们使用,代码中直接使用@Value("${yushengjun:test}")来获得
yushengjun配置的值,从v0.10.0开始的版本支持@Value("${yushengjun:test}") - 通过Java System Property
-
在运行时自动更新
我们来验证下我们的功能
整个应用的application.yml为
server: port: 8001 spring: application: name: springboot-abl eureka: client: service-url: defaultZone: http://localhost:8080/eureka apollo: cacheDir: d:\cache-dir cluster: default bootstrap: enabled: true namespaces: application,spring-rocketmq,spring-redis accesskey: secret: 5b02a3e3b9ee4a72aefe3786fc02b2fe1 property: order: enable: true
当前应用环境为dev环境,监控的集群是default集群,监控的namespace为application,spring-rocketmq,spring-redis
我们在,spring-rocketmq这个namespace下面修改了属性yushengjun的值,访问应用IndexController提供的端口,能够实时的更新配置的值
第二个注意点:
这里spring-rocketmq是一个私有的namespace,spring-redis是一个公共的namespace,里面都配置了yushengjun这个属性值,访问的时候
以私有的namespace的属性值有效
上面的配置中在应用weiyuantest下面有一个公共的namespace配置spring-redis,对于公共的namespace下面的值,其他应用的任何环境都是可以访问的
我们新建一个test22的应用来测试下
我们在weiyuantest应用下面修改了公共namespace的spring-redis下面yushengjun的值,访问test22应用能立刻获得公共的namespace的值,这里要注意
如果当前应用test22的私有的namespace下面存在yushengjun的值,那么就不会访问公共的spring-redis下面的值
接下来对于集群的验证
我们应用访问jiqun1下面的yushengjun的值,首先会访问jiqun1下面全部的私有namespace看是否存在yushengjun的值,如果没有就访问公共的namespace,如果公共的namespace的值都没有就直接返回。这里集群之间的配置是隔离的这里如果其他集群例如jiqun2里面存在yushengjun的值,这里也不会去访问的
这里还有一种情况,集群jiqun1下面application和spring-rocketmq两个私有的namespace都设置了yushengjun的值,访问的时候以哪个为准了
通过验证和应用配置namespace的顺序和关系,如下图所示
上面application的顺序高于spring-rocketmq的,就以application的值优先,如果spring-rocketmq的顺序高于application的,就以spring-rocketmq的为准
.注入多个namespace,并且指定顺序Spring的配置是有顺序的,如果多个property source都有同一个key,那么最终是顺序在前的配置生效。
注意点6:
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:07:25<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @RestController public class IndexController { @Value("${yushengjun:test}") private String yushengjun; @RequestMapping("/getYushengjun") public String getYushengjun() { return yushengjun; } }
默认情况下
Spring应用通常会使用Placeholder来注入配置,使用的格式形如${someKey:someDefaultValue},如${timeout:100}。冒号前面的是key,冒号后面的是默认值。
建议在实际使用时尽量给出默认值,以免由于key没有定义导致运行时错误。
从v0.10.0开始的版本支持placeholder在运行时自动更新,具体参见PR #972。
如果需要关闭placeholder在运行时自动更新功能,可以通过以下两种方式关闭:
通过设置System Property apollo.autoUpdateInjectedSpringProperties,如启动时传入-Dapollo.autoUpdateInjectedSpringProperties=false
通过设置META-INF/app.properties中的apollo.autoUpdateInjectedSpringProperties属性,如
app.id=SampleApp
apollo.autoUpdateInjectedSpringProperties=false
我们要看下代码
当我们这样设置之后,修改了配置的值,就不会自动更新了
注意点3:
@Value("${timeout:100}")能够实现一个属性值的更新,如果我们有多个属性值都需要更新,如果写多次@Value("${timeout:100}")比较麻烦,
我们可以把属性封装成一个对象,同一来进行处理
假设我有一个TestJavaConfigBean,通过Java Config的方式还可以使用@Value的方式注入:
package com.itmayiedu.api.controller; import org.springframework.beans.factory.annotation.Value; public class TestJavaConfigBean { @Value("${timeout:100}") private int timeout; private int batch; @Value("${batch:200}") public void setBatch(int batch) { this.batch = batch; } public int getTimeout() { return timeout; } public int getBatch() { return batch; } }
然后在启动类中按照下面的方式注入bean
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:09:14<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @EnableApolloConfig({"application", "spring-rocketmq","spring-redis"}) @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Bean public TestJavaConfigBean javaConfigBean() { return new TestJavaConfigBean(); } }
我们修改下IndexController
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:07:25<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @RestController public class IndexController { @Value("${yushengjun:test}") private String yushengjun; @Autowired private TestJavaConfigBean bean; @RequestMapping("/getYushengjun") public String getYushengjun() { return yushengjun; } @RequestMapping("/test") public String getYushengjun2() { return bean.getTimeout()+""; } }
当我们在配置中心页面上面修改了配置之后,访问http://localhost:8001/test,我们能够实时的得到配置变更的值
注意4:
使用@ConfigurationProperties 可以替换@value,但是需要注意的是,@ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEvent或RefreshScope
我们来看下具体的代码
首先我们定义一个配置类
package com.itmayiedu.api.controller; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.context.annotation.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; } }
这里的配置类SampleRedisConfig一定要能够被springboot启动类扫描到,默认springboot扫描启动类及其子包下面的配置
接下来第二个我们要增加一个配置类
package com.itmayiedu.api.controller; import com.ctrip.framework.apollo.core.ConfigConsts; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * apollo 自动刷新 * * @author qubianzhong * @Date 20:32 2019/11/11 */ @Component @Slf4j public class ApolloRefreshConfig implements ApplicationContextAware { @Autowired ApplicationContext applicationContext; @Autowired RefreshScope refreshScope; //application,spring-rocketmq,spring-redis //这里指定Apollo的namespace,非常重要,如果不指定,默认只使用application @ApolloConfigChangeListener(value = {ConfigConsts.NAMESPACE_APPLICATION,"application","spring-rocketmq"}) public void onChange(ConfigChangeEvent changeEvent) { for (String changedKey : changeEvent.changedKeys()) { System.err.println(changeEvent.getNamespace()+":"+changedKey+":"+changeEvent.getChange(changedKey)); log.error("apollo changed namespace:{} Key:{} value:{}", changeEvent.getNamespace(), changedKey, changeEvent.getChange(changedKey)); } refreshProperties(changeEvent); } public void refreshProperties(ConfigChangeEvent changeEvent) { this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); refreshScope.refreshAll(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
ApolloRefreshConfig这个类也是一定要能够被springboot启动类扫描到,默认springboot扫描启动类及其子包下面的配置,这里有几个注意点
@ApolloConfigChangeListener(value = {ConfigConsts.NAMESPACE_APPLICATION,"application","spring-rocketmq"}),这里要指定你要监控的
namespace
启动类需要把配置类注入到容器中
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:09:14<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @EnableApolloConfig({"application", "spring-rocketmq","spring-redis"}) @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Bean public TestJavaConfigBean javaConfigBean() { return new TestJavaConfigBean(); } @Bean public SampleRedisConfig sampleRedisConfig() { return new SampleRedisConfig(); } }
我们来看下调用的接口
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:07:25<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @RestController public class IndexController { @Value("${yushengjun:test}") private String yushengjun; @Autowired private TestJavaConfigBean bean; @Autowired private SampleRedisConfig bean2; @RequestMapping("/getYushengjun") public String getYushengjun() { return yushengjun; } @RequestMapping("/test") public String getYushengjun2() { return bean.getTimeout()+""; } @RequestMapping("/test2") public String getYushengjun22() { return bean2.getCommandTimeout()+""; } }
配置中心页面为
上图简要描述了Apollo客户端的实现原理:
- 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
- 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
- 这是一个fallback机制,为了防止推送机制失效导致配置不更新
- 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
- 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property:
apollo.refreshInterval
来覆盖,单位为分钟。
- 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
- 客户端会把从服务端获取到的配置在本地文件系统缓存一份
- 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
- 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
apollo本地开发模式
五、本地开发模式
Apollo客户端还支持本地开发模式,这个主要用于当开发环境无法连接Apollo服务器的时候,比如在邮轮、飞机上做相关功能开发。
在本地开发模式下,Apollo只会从本地文件读取配置信息,不会从Apollo服务器读取配置。
可以通过下面的步骤开启Apollo本地开发模式。
5.1 修改环境
修改/opt/settings/server.properties(Mac/Linux)或C:\opt\settings\server.properties(Windows)文件,设置env为Local:
env=Local
更多配置环境的方式请参考1.2.2 Environment
5.2 准备本地配置文件
在本地开发模式下,Apollo客户端会从本地读取文件,所以我们需要事先准备好配置文件。
5.2.1 本地配置目录
本地配置目录位于:
- Mac/Linux: /opt/data/{appId}/config-cache
- Windows: C:\opt\data\{appId}\config-cache
appId就是应用的appId,如100004458。
请确保该目录存在,且应用程序对该目录有读权限。
【小技巧】 推荐的方式是先在普通模式下使用Apollo,这样Apollo会自动创建该目录并在目录下生成配置文件。
5.2.2 本地配置文件
本地配置文件需要按照一定的文件名格式放置于本地配置目录下,文件名格式如下:
{appId}+{cluster}+{namespace}.properties
- appId就是应用自己的appId,如100004458
- cluster就是应用使用的集群,一般在本地模式下没有做过配置的话,就是default
- namespace就是应用使用的配置namespace,一般是application
文件内容以properties格式存储,比如如果有两个key,一个是request.timeout,另一个是batch,那么文件内容就是如下格式:
request.timeout=2000
batch=2000
5.3 修改配置
在本地开发模式下,Apollo不会实时监测文件内容是否有变化,所以如果修改了配置,需要重启应用生效。
我们来验证下,第一步修改server.properties的内容
env=Local
接下来修改缓存文件,我的缓存文件目录位于D:\cache-dir\weiyuantest\config-cache目录下
这里集群为jiqun1,对于的namespace为application、spring-rocketmq,所以这里修改缓存配置weiyuantest+jiqun1+application.properties
这样修改完成就可以了,应用就只用本地缓存的配置文件
接下来我们来验证下面的一个功能:配置中心Apollo存储加密字段
应用中需要加入jasypt的依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itmayiedu</groupId> <artifactId>springboot-abl</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </exclusion> </exclusions> --> </dependency> <!-- apollo 携程apollo配置中心框架 --> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-core</artifactId> <version>1.7.0</version> </dependency> <!--jasypt加密 --> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>1.16</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-conf</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <encoding>UTF-8</encoding> <outputDirectory>${project.build.directory}/ext/conf</outputDirectory> <resources> <resource> <directory>ext/conf</directory> <includes> <include>logback.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.5.201505241946</version> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>default-prepare-agent-integration</id> <goals> <goal>prepare-agent-integration</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.3</version> <configuration> <imageName>hy_uav_gateway</imageName> <dockerDirectory>src/main/docker</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> <include>ext/conf/logback.xml</include> </resource> </resources> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
接下来需要在应用的application.yml中进行设置
server: port: 8001 spring: application: name: springboot-abl apollo: cacheDir: d:\cache-dir cluster: jiqun1 bootstrap: enabled: true namespaces: application,spring-rocketmq,spring-redis accesskey: secret: 5b02a3e3b9ee4a72aefe3786fc02b2fe1 refreshInterval: 1 property: order: enable: true jasypt: encryptor: password: yinjihaunkey
上面配置的就是加密和解密的key,jasypt.encryptor.password:配置加密的Key
创建一个加密的工具类,用于加密配置:
package com.itmayiedu.api.controller; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI; import com.ctrip.framework.apollo.core.utils.StringUtils; public class EncryptUtil { /** * 制表符、空格、换行符 PATTERN */ private static Pattern BLANK_PATTERN = Pattern.compile("\\s*|\t|\r|\n"); /** * 加密Key */ private static String PASSWORD = "yinjihaunkey"; /** * 加密算法 */ private static String ALGORITHM = "PBEWithMD5AndDES"; public static Map<String, String> getEncryptedParams(String input) { //输出流 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024); PrintStream cacheStream = new PrintStream(byteArrayOutputStream); //更换数据输出位置 System.setOut(cacheStream); //加密参数组装 String[] args = {"input=" + input, "password=" + PASSWORD, "algorithm=" + ALGORITHM}; JasyptPBEStringEncryptionCLI.main(args); //执行加密后的输出 String message = byteArrayOutputStream.toString(); String str = replaceBlank(message); int index = str.lastIndexOf("-"); //返回加密后的数据 Map<String, String> result = new HashMap<String, String>(); result.put("input", str.substring(index + 1)); result.put("password", PASSWORD); return result; } /** * 替换制表符、空格、换行符 * * @param str * @return */ private static String replaceBlank(String str) { String dest = ""; if (!StringUtils.isEmpty(str)) { Matcher matcher = BLANK_PATTERN.matcher(str); dest = matcher.replaceAll(""); } return dest; } public static void main(String[] args) { System.out.println(getEncryptedParams("hello2222222222222")); } }
我们运行可以看到明文hello2222222222222使用秘钥yinjihaunkey加密之后的密文变为了A8RyPX5rqBuQ6O/CSY7GF5kTUqfhZiylVJ6JFdNdyxE=
{input=A8RyPX5rqBuQ6O/CSY7GF5kTUqfhZiylVJ6JFdNdyxE=, password=yinjihaunkey}
nput就是hello2222222222222加密之后的内容,将input的值复制存储到Apollo中,存储的格式需要按照一定的规则才行:
ENC(A8RyPX5rqBuQ6O/CSY7GF5kTUqfhZiylVJ6JFdNdyxE=)
需要将加密的内容用ENC包起来,这样jasypt才会去解密这个值。
其它地方的代码不需要做任何修改
@Value("${yushengjun:test}")
private String yushengjun;
在配置中心下发ENC(A8RyPX5rqBuQ6O/CSY7GF5kTUqfhZiylVJ6JFdNdyxE=)这个配置项之后,应用收到ENC(A8RyPX5rqBuQ6O/CSY7GF5kTUqfhZiylVJ6JFdNdyxE=)这个配置项,因为这个配置项是ENC开头的,jasypt框架就会使用application.yml里面jasypt:encryptor: password这个秘钥来解密,应用使用的是明文
我们来看看配置中心
配置项在数据库中也是以密文的形式保存的
apollo对日志级别的动态调整
编写一个配置类,这个类一定也要被springboot的配置类扫描到
package com.itmayiedu.api.controller; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfig; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import java.util.Set; /** * @author ChengJianSheng * @date 2019-05-31 */ @Slf4j @Configuration public class LoggerConfig { private static final String LOGGER_TAG = "logging.level."; /** * 注入默认的命名空间配置 */ @ApolloConfig private Config config; @Autowired private LoggingSystem loggingSystem; @ApolloConfigChangeListener private void onChange(ConfigChangeEvent configChangeEvent) { System.out.println("配置发生变化"); System.out.println("Changes for namespace " + configChangeEvent.getNamespace()); for (String key : configChangeEvent.changedKeys()) { ConfigChange change = configChangeEvent.getChange(key); System.out.println(String.format("Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType())); } Set<String> keyNames = config.getPropertyNames(); for (String key : keyNames) { if (StringUtils.isEmpty(key)) { continue; } if (!key.startsWith(LOGGER_TAG)) { continue; } String loggerName = key.replace(LOGGER_TAG, ""); String strLevel = config.getProperty(key, "info"); LogLevel level = LogLevel.valueOf(strLevel.toUpperCase()); loggingSystem.setLogLevel(loggerName, level); log.info("{}:{}", key, strLevel); } } }
实现起来超级简单,使用spring boot自带的LoggingSystem的api来动态设置日志级别,当然的项目需要提供动态调整的接口,来达动态调整的目的,这里注意的是 @ApolloConfigChangeListener监听的是默认的namespace的是application,这里一定要注意下
怎么配置?
和在spring环境下正常配置日志级别一样配置即可,如
logging.level.org.springframework = info
logging.level.com.yudianbank.sales = debug
logging.level.org.hibernate = info
如上代表spring体系工具开启INFO级别日志,业务系统开启DEBUG级别日志,hibernate开启INFO级别日志
然后在application.yml的配置文件中一定要增加下面的配置
从1.2.0版本开始,如果希望把日志相关的配置(如logging.level.root=info
或logback-spring.xml
中的参数)也放在Apollo管理,那么可以额外配置apollo.bootstrap.eagerLoad.enabled=true
来使Apollo的加载顺序放到日志系统加载之前,不过这会导致Apollo的启动过程无法通过日志的方式输出(因为执行Apollo加载的时候,日志系统压根没有准备好呢!所以在Apollo代码中使用Slf4j的日志输出便没有任何内容),更多信息可以参考PR 1614。参考配置示例如下:
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
# put apollo initialization before logging system initialization
apollo.bootstrap.eagerLoad.enabled=true
整个配置文件如下
server: port: 8001 spring: application: name: springboot-abl apollo: cacheDir: d:\cache-dir cluster: jiqun1 bootstrap: enabled: true namespaces: application,spring-rocketmq,spring-redis eagerLoad: enabled: true accesskey: secret: 5b02a3e3b9ee4a72aefe3786fc02b2fe1 refreshInterval: 1 property: order: enable: true jasypt: encryptor: password: yinjihaunkey
我们来模拟下日志的打印
/** * 功能说明: * 功能作者: * 创建日期: * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com */ package com.itmayiedu.api.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import lombok.extern.slf4j.Slf4j; /** * 功能说明: <br> * 创建作者:每特教育-余胜军<br> * 创建时间:2018年8月28日 下午9:07:25<br> * 教育机构:每特教育|蚂蚁课堂<br> * 版权说明:上海每特教育科技有限公司版权所有<br> * 官方网站:www.itmayiedu.com|www.meitedu.com<br> * 联系方式:qq644064779<br> * 注意:本内容有每特教育学员共同研发,请尊重原创版权 */ @Slf4j @RestController public class IndexController { @Value("${yushengjun:test}") private String yushengjun; @Autowired private TestJavaConfigBean bean; @Autowired private SampleRedisConfig bean2; @RequestMapping("/getYushengjun") public String getYushengjun() { log.info("123456hjhhjhjhj"); log.debug("123456hjhhjhjhj"); log.warn("123456hjhhjhjhj"); log.error("123456hjhhjhjhj"); return yushengjun; } @RequestMapping("/test") public String getYushengjun2() { return bean.getTimeout()+""; } @RequestMapping("/test2") public String getYushengjun22() { return bean2.getCommandTimeout()+""; } }
我们在配置中心的页面上进行调整
如果要调整hibernate的日志打印,只需要写成
logging.level.org.hibernate = info
这样就可以了
apollo的回滚发布机制:
如果发现已发布的配置有问题,可以通过点击『回滚』按钮来将客户端读取到的配置回滚到上一个发布版本。
这里的回滚机制类似于发布系统,发布系统中的回滚操作是将部署到机器上的安装包回滚到上一个部署的版本,但代码仓库中的代码是不会回滚的,从而开发可以在修复代码后重新发布。
Apollo中的回滚也是类似的机制,点击回滚后是将发布到客户端的配置回滚到上一个已发布版本,也就是说客户端读取到的配置会恢复到上一个版本,但页面上编辑状态的配置是不会回滚的,从而开发可以在修复配置后重新发布。
这里当前的日志级别的值是info,回退到上一版本的话就是error,回滚之后客户端读取的值是error,但是配置中心的页面上显示的还是info,效果如下
apollo安全权限的设置:
1.2 项目权限分配
1.2.1 项目管理员权限
项目管理员拥有以下权限:
- 可以管理项目的权限分配
- 可以创建集群
- 可以创建Namespace
创建项目时填写的应用负责人默认会成为项目的管理员之一,如果还需要其他人也成为项目管理员,可以按照下面步骤操作:
-
点击页面左侧的“管理项目”
-
搜索需要添加的成员并点击添加
1.2.2 配置编辑、发布权限
配置权限分为编辑和发布:
- 编辑权限允许用户在Apollo界面上创建、修改、删除配置
- 配置修改后只在Apollo界面上变化,不会影响到应用实际使用的配置
- 发布权限允许用户在Apollo界面上发布、回滚配置
- 配置只有在发布、回滚动作后才会被应用实际使用到
- Apollo在用户操作发布、回滚动作后实时通知到应用,并使最新配置生效
项目创建完,默认没有分配配置的编辑和发布权限,需要项目管理员进行授权。
-
点击application这个namespace的授权按钮
-
分配修改权限
-
分配发布权限
Apollo 默认允许所有登录用户创建项目,如果只允许部分用户创建项目,可以开启创建项目权限控制
11. role.manage-app-master.enabled - 是否开启项目管理员分配权限控制
适用于1.5.0及以上版本
默认为false,所有项目的管理员可以为项目添加/删除管理员
如果设置为true,那么只有超级管理员和拥有项目管理员分配权限的帐号可以为特定项目添加/删除管理员,超级管理员可以通过管理员工具 - 系统权限管理
给用户分配特定项目的管理员分配权限
接下来第二个设置:
配置apollo-adminservice是否开启访问控制,,如果开启之后,apollo的portal访问的时候必须携带对应的token的值
现在我们配置dev环境的adminserver开启权限访问,需要在对于的数据库中
apolloconfigdb-dev,如果是开启pro环境的,需要修改apolloconfigdb-pro
接下来,需要在portal中进行配置,portal中需要携带上面的dev的token才能访问dev的环境, 调整ApolloConfigDB配置
配置之后修改如下
第二个配置2:
2.1.3.2 调整ApolloConfigDB配置
我们来配置下dev环境下面,我们开启配置一次发布只能有一个人修改,另外一个人发布的功能,避免出现配置出现问题
我们俩看下开启之后的效果为
对于配置2:
3. config-service.cache.enabled - 是否开启配置缓存
这是一个功能开关,如果配置为true的话,config service会缓存加载过的配置信息,从而加快后续配置获取性能。
默认为false,开启前请先评估总配置大小并调整config service内存配置。
开启缓存后必须确保应用中配置的app.id大小写正确,否则将获取不到正确的配置
附件:和ApolloConfigDB配置相关的配置如下
2.1.3.2 调整ApolloConfigDB配置 配置项统一存储在ApolloConfigDB.ServerConfig表中,需要注意每个环境的ApolloConfigDB.ServerConfig都需要单独配置,修改完一分钟实时生效。 1. eureka.service.url - Eureka服务Url 不适用于基于Kubernetes原生服务发现场景 不管是apollo-configservice还是apollo-adminservice都需要向eureka服务注册,所以需要配置eureka服务地址。 按照目前的实现,apollo-configservice本身就是一个eureka服务,所以只需要填入apollo-configservice的地址即可,如有多个,用逗号分隔(注意不要忘了/eureka/后缀)。 需要注意的是每个环境只填入自己环境的eureka服务地址,比如FAT的apollo-configservice是1.1.1.1:8080和2.2.2.2:8080,UAT的apollo-configservice是3.3.3.3:8080和4.4.4.4:8080,PRO的apollo-configservice是5.5.5.5:8080和6.6.6.6:8080,那么: 在FAT环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为: http://1.1.1.1:8080/eureka/,http://2.2.2.2:8080/eureka/ 在UAT环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为: http://3.3.3.3:8080/eureka/,http://4.4.4.4:8080/eureka/ 在PRO环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为: http://5.5.5.5:8080/eureka/,http://6.6.6.6:8080/eureka/ 注1:这里需要填写本环境中全部的eureka服务地址,因为eureka需要互相复制注册信息 注2:如果希望将Config Service和Admin Service注册到公司统一的Eureka上,可以参考部署&开发遇到的常见问题 - 将Config Service和Admin Service注册到单独的Eureka Server上章节 注3:在多机房部署时,往往希望config service和admin service只向同机房的eureka注册,要实现这个效果,需要利用ServerConfig表中的cluster字段,config service和admin service会读取所在机器的/opt/settings/server.properties(Mac/Linux)或C:\opt\settings\server.properties(Windows)中的idc属性,如果该idc有对应的eureka.service.url配置,那么就只会向该机房的eureka注册。比如config service和admin service会部署到SHAOY和SHAJQ两个IDC,那么为了实现这两个机房中的服务只向该机房注册,那么可以在ServerConfig表中新增两条记录,分别填入SHAOY和SHAJQ两个机房的eureka地址即可,default cluster的记录可以保留,如果有config service和admin service不是部署在SHAOY和SHAJQ这两个机房的,就会使用这条默认配置。 Key Cluster Value Comment eureka.service.url default http://1.1.1.1:8080/eureka/ 默认的Eureka服务Url eureka.service.url SHAOY http://2.2.2.2:8080/eureka/ SHAOY的Eureka服务Url eureka.service.url SHAJQ http://3.3.3.3:8080/eureka/ SHAJQ的Eureka服务Url 2. namespace.lock.switch - 一次发布只能有一个人修改开关,用于发布审核 这是一个功能开关,如果配置为true的话,那么一次配置发布只能是一个人修改,另一个发布。 生产环境建议开启此选项 3. config-service.cache.enabled - 是否开启配置缓存 这是一个功能开关,如果配置为true的话,config service会缓存加载过的配置信息,从而加快后续配置获取性能。 默认为false,开启前请先评估总配置大小并调整config service内存配置。 开启缓存后必须确保应用中配置的app.id大小写正确,否则将获取不到正确的配置 4. item.key.length.limit - 配置项 key 最大长度限制 默认配置是128。 5. item.value.length.limit - 配置项 value 最大长度限制 默认配置是20000。 6. admin-service.access.control.enabled - 配置apollo-adminservice是否开启访问控制 适用于1.7.1及以上版本 默认为false,如果配置为true,那么apollo-portal就需要正确配置访问该环境的access token,否则访问会被拒绝 7. admin-service.access.tokens - 配置允许访问apollo-adminservice的access token列表 适用于1.7.1及以上版本 如果该配置项为空,那么访问控制不会生效。如果允许多个token,token 之间以英文逗号分隔 样例: admin-service.access.tokens=098f6bcd4621d373cade4e832627b4f6 admin-service.access.tokens=098f6bcd4621d373cade4e832627b4f6,ad0234829205b9033196ba818f7a872b
附件:和ApolloConfigDB配置相关的配置如下
2.1.3.2 调整ApolloConfigDB配置 配置项统一存储在ApolloConfigDB.ServerConfig表中,需要注意每个环境的ApolloConfigDB.ServerConfig都需要单独配置,修改完一分钟实时生效。 1. eureka.service.url - Eureka服务Url 不适用于基于Kubernetes原生服务发现场景 不管是apollo-configservice还是apollo-adminservice都需要向eureka服务注册,所以需要配置eureka服务地址。 按照目前的实现,apollo-configservice本身就是一个eureka服务,所以只需要填入apollo-configservice的地址即可,如有多个,用逗号分隔(注意不要忘了/eureka/后缀)。 需要注意的是每个环境只填入自己环境的eureka服务地址,比如FAT的apollo-configservice是1.1.1.1:8080和2.2.2.2:8080,UAT的apollo-configservice是3.3.3.3:8080和4.4.4.4:8080,PRO的apollo-configservice是5.5.5.5:8080和6.6.6.6:8080,那么: 在FAT环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为: http://1.1.1.1:8080/eureka/,http://2.2.2.2:8080/eureka/ 在UAT环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为: http://3.3.3.3:8080/eureka/,http://4.4.4.4:8080/eureka/ 在PRO环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为: http://5.5.5.5:8080/eureka/,http://6.6.6.6:8080/eureka/ 注1:这里需要填写本环境中全部的eureka服务地址,因为eureka需要互相复制注册信息 注2:如果希望将Config Service和Admin Service注册到公司统一的Eureka上,可以参考部署&开发遇到的常见问题 - 将Config Service和Admin Service注册到单独的Eureka Server上章节 注3:在多机房部署时,往往希望config service和admin service只向同机房的eureka注册,要实现这个效果,需要利用ServerConfig表中的cluster字段,config service和admin service会读取所在机器的/opt/settings/server.properties(Mac/Linux)或C:\opt\settings\server.properties(Windows)中的idc属性,如果该idc有对应的eureka.service.url配置,那么就只会向该机房的eureka注册。比如config service和admin service会部署到SHAOY和SHAJQ两个IDC,那么为了实现这两个机房中的服务只向该机房注册,那么可以在ServerConfig表中新增两条记录,分别填入SHAOY和SHAJQ两个机房的eureka地址即可,default cluster的记录可以保留,如果有config service和admin service不是部署在SHAOY和SHAJQ这两个机房的,就会使用这条默认配置。 Key Cluster Value Comment eureka.service.url default http://1.1.1.1:8080/eureka/ 默认的Eureka服务Url eureka.service.url SHAOY http://2.2.2.2:8080/eureka/ SHAOY的Eureka服务Url eureka.service.url SHAJQ http://3.3.3.3:8080/eureka/ SHAJQ的Eureka服务Url 2. namespace.lock.switch - 一次发布只能有一个人修改开关,用于发布审核 这是一个功能开关,如果配置为true的话,那么一次配置发布只能是一个人修改,另一个发布。 生产环境建议开启此选项 3. config-service.cache.enabled - 是否开启配置缓存 这是一个功能开关,如果配置为true的话,config service会缓存加载过的配置信息,从而加快后续配置获取性能。 默认为false,开启前请先评估总配置大小并调整config service内存配置。 开启缓存后必须确保应用中配置的app.id大小写正确,否则将获取不到正确的配置 4. item.key.length.limit - 配置项 key 最大长度限制 默认配置是128。 5. item.value.length.limit - 配置项 value 最大长度限制 默认配置是20000。 6. admin-service.access.control.enabled - 配置apollo-adminservice是否开启访问控制 适用于1.7.1及以上版本 默认为false,如果配置为true,那么apollo-portal就需要正确配置访问该环境的access token,否则访问会被拒绝 7. admin-service.access.tokens - 配置允许访问apollo-adminservice的access token列表 适用于1.7.1及以上版本 如果该配置项为空,那么访问控制不会生效。如果允许多个token,token 之间以英文逗号分隔 样例: admin-service.access.tokens=098f6bcd4621d373cade4e832627b4f6 admin-service.access.tokens=098f6bcd4621d373cade4e832627b4f6,ad0234829205b9033196ba818f7a872b
附件:和ApolloPortalDB配置相关的配置如下
Apollo自身的一些配置是放在数据库里面的,所以需要针对实际情况做一些调整。 以下配置除了支持在数据库中配置以外,也支持通过-D参数、application.properties等配置,且-D参数、application.properties等优先级高于数据库中的配置 2.1.3.1 调整ApolloPortalDB配置 配置项统一存储在ApolloPortalDB.ServerConfig表中,也可以通过管理员工具 - 系统参数页面进行配置,无特殊说明则修改完一分钟实时生效。 1. apollo.portal.envs - 可支持的环境列表 默认值是dev,如果portal需要管理多个环境的话,以逗号分隔即可(大小写不敏感),如: DEV,FAT,UAT,PRO 修改完需要重启生效。 注1:一套Portal可以管理多个环境,但是每个环境都需要独立部署一套Config Service、Admin Service和ApolloConfigDB,具体请参考:2.1.2 创建ApolloConfigDB,2.1.3.2 调整ApolloConfigDB配置,2.2.1.1.2 配置数据库连接信息,另外如果是为已经运行了一段时间的Apollo配置中心增加环境,别忘了参考2.1.2.1 从别的环境导入ApolloConfigDB的项目数据对新的环境做初始化。 注2:只在数据库添加环境是不起作用的,还需要为apollo-portal添加新增环境对应的meta server地址,具体参考:2.2.1.1.2.4 配置apollo-portal的meta service信息。apollo-client在新的环境下使用时也需要做好相应的配置,具体参考:1.2.2 Apollo Meta Server。 注3:如果希望添加自定义的环境名称,具体步骤可以参考Portal如何增加环境。 注4:1.1.0版本增加了系统信息页面(管理员工具 -> 系统信息),可以通过该页面检查配置是否正确 2. apollo.portal.meta.servers - 各环境Meta Service列表 适用于1.6.0及以上版本 Apollo Portal需要在不同的环境访问不同的meta service(apollo-configservice)地址,所以我们需要在配置中提供这些信息。默认情况下,meta service和config service是部署在同一个JVM进程,所以meta service的地址就是config service的地址。 样例如下: { "DEV":"http://1.1.1.1:8080", "FAT":"http://apollo.fat.xxx.com", "UAT":"http://apollo.uat.xxx.com", "PRO":"http://apollo.xxx.com" } 修改完需要重启生效。 该配置优先级高于其它方式设置的Meta Service地址,更多信息可以参考2.2.1.1.2.4 配置apollo-portal的meta service信息。 3. organizations - 部门列表 Portal中新建的App都需要选择部门,所以需要在这里配置可选的部门信息,样例如下: [{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}] 4. superAdmin - Portal超级管理员 超级管理员拥有所有权限,需要谨慎设置。 如果没有接入自己公司的SSO系统的话,可以先暂时使用默认值apollo(默认用户)。等接入后,修改为实际使用的账号,多个账号以英文逗号分隔(,)。 5. consumer.token.salt - consumer token salt 如果会使用开放平台API的话,可以设置一个token salt。如果不使用,可以忽略。 6. wiki.address portal上“帮助”链接的地址,默认是Apollo github的wiki首页,可自行设置。 7. admin.createPrivateNamespace.switch 是否允许项目管理员创建private namespace。设置为true允许创建,设置为false则项目管理员在页面上看不到创建private namespace的选项。了解更多Namespace 8. emergencyPublish.supported.envs 配置允许紧急发布的环境列表,多个env以英文逗号分隔。 当config service开启一次发布只能有一个人修改开关(namespace.lock.switch)后,一次配置发布只能是一个人修改,另一个发布。为了避免遇到紧急情况时(如非工作时间、节假日)无法发布配置,可以配置此项以允许某些环境可以操作紧急发布,即同一个人可以修改并发布配置。 9. configView.memberOnly.envs 只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔。 对设定了只对项目成员显示配置信息的环境,只有该项目的管理员或拥有该namespace的编辑或发布权限的用户才能看到该私有namespace的配置信息和发布历史。公共namespace始终对所有用户可见。 从1.1.0版本开始支持,详见PR 1531 10. role.create-application.enabled - 是否开启创建项目权限控制 适用于1.5.0及以上版本 默认为false,所有用户都可以创建项目 如果设置为true,那么只有超级管理员和拥有创建项目权限的帐号可以创建项目,超级管理员可以通过管理员工具 - 系统权限管理给用户分配创建项目权限 11. role.manage-app-master.enabled - 是否开启项目管理员分配权限控制 适用于1.5.0及以上版本 默认为false,所有项目的管理员可以为项目添加/删除管理员 如果设置为true,那么只有超级管理员和拥有项目管理员分配权限的帐号可以为特定项目添加/删除管理员,超级管理员可以通过管理员工具 - 系统权限管理给用户分配特定项目的管理员分配权限 12. admin-service.access.tokens - 设置apollo-portal访问各环境apollo-adminservice所需的access token 适用于1.7.1及以上版本 如果对应环境的apollo-adminservice开启了访问控制,那么需要在此配置apollo-portal访问该环境apollo-adminservice所需的access token,否则会访问失败 格式为json,如下所示: { "dev" : "098f6bcd4621d373cade4e832627b4f6", "pro" : "ad0234829205b9033196ba818f7a872b" }
posted on 2020-09-02 14:00 luzhouxiaoshuai 阅读(1507) 评论(0) 编辑 收藏 举报