版本
- spring-boot 2.0.6.RELEASE
- spring-cloud Finchley.SR2
目录结构
eurka-server 服务注册中心
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.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.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</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>
</plugins>
</build>
application.yml
server:
port: 18080
spring:
application:
name: eurka-server
eureka:
instance:
hostname: localhost # 主机地址
client:
register-with-eureka: false # 注册中心告诉自己不能向自己注册自己,默认为true
fetch-registry: false # 获取注册信息
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false # 关闭自我保护
eviction-interval-timer-in-ms: 60000 # 清理时间间隔 服务器清理服务列表的定时器 单位 毫秒
注册中心 启动类 EurekaServerApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer //表示是一个 eureka 服务
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
在自己的git上上传一个配置文件(在统一配置中心config-server 配置git仓库地址时需要使用这个作为远程配置文件仓库)
config-server 统一配置中心
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.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.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!--配置中心监控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-monitor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</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>
</plugins>
</build>
application.yml
server:
port: 18082
spring:
application:
name: config-server
cloud:
config:
server:
git:
search-paths: /** #远程仓库的目录,本例子配置文件在根目录中
uri: https://github.com/wangqiang/MySpringCloudConfig.git # 配置自己的git仓库地址
#username: '' # 访问git仓库的用户名 如果Git仓库为公开仓库,可以不填写用户名和密码,如果是私有仓库需要填写,
#password: '' # 访问git仓库的用户密码
default-label: master # 配置仓库的分支
#消息总线配置
bus:
enabled: true
trace:
enabled: true
refresh:
enabled: true #开启总线消息更新功能
# rabbitmq 配置
rabbitmq:
host: 127.0.0.1
port: 5672
username: rabbit
password: 123456
virtual-host: /
eureka:
client:
service-url:
defaultZone: http://localhost:18080/eureka/ #注册中心地址
registry-fetch-interval-seconds: 20 # 多久拉取服务注册信息 表示从“发现”服务器获取注册表信息的频率(以秒为单位)。
instance:
# 60s未收到心跳,剔除instance 要比心跳时间大 服务器端等待的时间,因为它收到了最后的心跳,然后才可以从它的视图中删除这个实例,并且不允许流量进入这个实例。将这个值设置得太长可能意味着,即使实例不存在,流量也可以被路由到实例。将这个值设置得太小可能意味着,由于临时网络故障,该实例可能会被排除在流量之外。这个值的设置至少要高于leaseRenewalIntervalInSeconds中指定的值。默认90s
lease-expiration-duration-in-seconds: 60
# 心跳时间 即每15秒发送一次心跳 表明客户端需要将心跳发送到服务器端,以表明它还活着。如果心跳停止的时间超过了服务器设置的等待时间,那么服务器端将会从它的服务列表中删除该实例,从而将流量排除在该实例之外。默认30s
lease-renewal-interval-in-seconds: 15
prefer-ip-address: true
ip-address: 127.0.0.1
hostname: localhost
instance-id: ${spring.cloud.client.ip-address}:${server.port} #eureka client向eureka server注册使用真实ip设置 默认为本机机器名
# 屏蔽安全验证
management:
endpoints:
web:
exposure:
include: '*' # 可以选择【"health","mappings","bus-refresh"】三种选项暴露那些端点
统一配置中心 启动类 ConfigServerApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableConfigServer //标记为 Config Server
@EnableEurekaClient //标记为eureka client
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
auth-server 业务的微服务
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.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.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<!-- 排除默认日志插件 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除默认日志插件 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
<!-- 使用log4j2 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- Log4j2 异步支持 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
<!-- sql 日志待参数输出 -->
<dependency>
<groupId>com.googlecode.log4jdbc</groupId>
<artifactId>log4jdbc</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<!-- 排除默认日志插件 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</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>
<dependencies>
<!-- spring热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.8.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
bootstrap.yml
配置中心的客户端要使用bootstrap.yml
bootstrap.yml优先于 application.yml 加载 即 bootstrap.yml加载完后才加载 application.yml
server:
tomcat:
uri-encoding: UTF-8
port: 18088
spring:
application:
name: auth-server # 对应配置中心配置文件中的{application}部分 名称需要与子服务名称以及读取的配置文件库中文件名称一致
cloud:
config:
discovery:
enabled: true # 配置中心的servieId,即服务名
service-id: config-server # 配置中心的服务名
label: master # 指明远程仓库的分支
profile: dev # 对应配置中心配置文件中的{profile}部分 本例子中读取的配置文件为 auth-server-dev.yml dev开发环境配置文件 test测试环境 pro正式环境
name: auth-server # 对应配置中心配置文件中的{application}部分 多个用,隔开
fail-fast: true
#消息总线配置
bus:
enabled: true
trace:
enabled: true
refresh:
enabled: true #开启总线消息更新功能
eureka:
client:
service-url:
defaultZone: http://localhost:18080/eureka/ #注册中心地址
registry-fetch-interval-seconds: 20 # 多久拉取服务注册信息 表示从“发现”服务器获取注册表信息的频率(以秒为单位)。
instance:
# 60s未收到心跳,剔除instance 要比心跳时间大 服务器端等待的时间,因为它收到了最后的心跳,然后才可以从它的视图中删除这个实例,并且不允许流量进入这个实例。将这个值设置得太长可能意味着,即使实例不存在,流量也可以被路由到实例。将这个值设置得太小可能意味着,由于临时网络故障,该实例可能会被排除在流量之外。这个值的设置至少要高于leaseRenewalIntervalInSeconds中指定的值。默认90s
lease-expiration-duration-in-seconds: 60
# 心跳时间 即每15秒发送一次心跳 表明客户端需要将心跳发送到服务器端,以表明它还活着。如果心跳停止的时间超过了服务器设置的等待时间,那么服务器端将会从它的服务列表中删除该实例,从而将流量排除在该实例之外。默认30s
lease-renewal-interval-in-seconds: 15
prefer-ip-address: true
ip-address: 127.0.0.1
hostname: localhost
instance-id: ${spring.cloud.client.ip-address}:${server.port} #eureka client向eureka server注册使用真实ip设置 默认为本机机器名
# 屏蔽安全验证
management:
endpoints:
web:
exposure:
include: '*' # 可以选择【"health","mappings","bus-refresh"】三种选项暴露那些端点
业务微服务端 启动类 AuthServerApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
}
业务微服务端 测试类
在需要更新的配置类上加@RefreshScope注解,@RefreshScope必须加,否则客户端会收到服务端的更新消息,但是更新不了,因为不知道更新哪里的。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope // 需要更新的配置类上加@RefreshScope注解,@RefreshScope必须加,否则客户端会收到服务端的更新消息,但是更新不了,因为不知道更新哪里的
public class Test {
@Value("${name}")
private String name;
@GetMapping("test")
public String test() {
return name;
}
}
依次启动 eurka-server -> config-server -> auth-server
访问远程git上的配置文件 http://127.0.0.1:18082/auth-server/dev
18082: 是统一配置中心的端口
/auth-server/dev 对应远程配置文件的auth-server-dev.yml
/{name}-{profiles}.yml
/{label}/{name}-{profiles}.yml
name : 文件名,一般以服务名来命名
profiles : 一般作为环境标识
lable : 分支(branch),指定访问某分支下的配置文件
{
"name": "auth-server",
"profiles": [
"dev"
],
"label": null,
"version": "69b2e80b7f8dbd6f84f24152a389bf091d5333dc",
"state": null,
"propertySources": [
{
"name": "https://github.com/wangqiang/MySpringCloudConfig.git/auth-server-dev.yml",
"source": {
"name": "之歌刘",
"spring.rabbitmq.host": "127.0.0.1",
"spring.rabbitmq.port": 5672,
"spring.rabbitmq.username": "rabbit",
"spring.rabbitmq.password": 123456,
"spring.rabbitmq.virtual-host": "/"
}
}
]
}
业务微服务端 获取远程配置文件中的属性值
业务微服端的测试接口:http://127.0.0.1:18088/test
远程配置文件更新后,客户端自动更新
修改配置文件并提交到git上
name: 啊哈哈哈哈
- 首先发布一个post请求 http://localhost:18088/actuator/bus-refresh 手动刷新配置
或者请求 http://localhost::18082/actuator/refresh
18088 是配置客户端应用 config-client
18082 是配置服务中心 config-server
- 再次访问测试接口 http://127.0.0.1:18088/test
我们发现再不重启服务的情况下就能获取到修改的属性值
只要开启 Spring Cloud Bus 后,不管是对 config-server 还是 config-client 执行/actuator/bus-refresh都是可以更新配置的,
如果有多个客户端,多个客户端都会接收到刷新配置的消息,并刷新配置。
局部刷新配置(只刷新某些客户端)
某些场景下(例如灰度发布),我们可能只想刷新部分微服务的配置,此时可通过/actuator/bus-refresh/{destination}端点的 destination 参数来定位要刷新的应用程序。例如:/actuator/bus-refresh/customers:8000,这样消息总线上的微服务实例就会根据 destination 参数的值来判断是否需要要刷新。其中,customers:8000指的是各个微服务的 ApplicationContext ID。destination 参数也可以用来定位特定的微服务。例如:/actuator/bus-refresh/customers:**,这样就可以触发 customers 微服务所有实例的配置刷新。
集成WebHooks实现动态更新
远程配置文件修改后,不需要手动调用http://localhost:18088/actuator/bus-refresh 即可刷新配置
-
在远程配置文件的MySpringCloudConfig.git仓库中添加密钥
-
在远程配置文件git仓库中找到Webhooks
-
点击Add webhook,添加一个webhook
Payload URL: push 代码后自动运行的脚本 http://localhost:18082/actuator/bus-refresh(配置能够被git访问到的请求地址,根据git内外网情况自行配置)
Content type :我们选择application/json
Secret: 通讯密钥 用来认证身份
选择Just the push event.,因为我们只需要push的时候进行回调,然后添加即可
每当我们更新远程配置文件后,push到git后,就会触发Payload URL(配置的是访问刷新配置的地址)