1.config
1.1定义
config的功能是对配置文件进行集中式的管理,它为微服务提供集中化的外部配置支持,配置服务器为各个不同的微服务的所有环境提供了中心化的外部配置,分为服务端和客户端。
它作为配置中心,主要在微服务和远程仓库之间,进行数据的交互。也就是说把配置文件统一放在远程仓库(如github)上,配置中心去拉取远程配置文件到本地,微服务需要配置时找配置中心索要对应的配置即可,方便维护配置信息,实现动态配置实时生效。
1.2基础环境搭建
源代码:https://github.com/zhongyushi-git/cloud-config-demo.git
1)创建一个maven工程名为cloud-config-demo,作为父工程,删除src目录
2)在pom中导入依赖,对SpringBoot和SpringCloud版本进行锁定
<properties> <spring.boot.version>2.2.2.RELEASE</spring.boot.version> <spring.cloud.version>Hoxton.SR1</spring.cloud.version> </properties> <!-- 依赖管理,父工程锁定版本--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <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>
1.3服务端配置
1)在github上新建一个仓库,名为spring-cloud-server-config。新建一个文件test.properties,内容如下:
info=hello world
此文件只作为本章节测试使用。另外配置文件后期都是在本地仓库创建后提交到远程仓库,不采用在线编辑的方式。
2)新建子模块(cloud-config-server3344),导入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <!--config-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> </dependencies>
3)创建启动类并加@EnableConfigServer注解
package com.zys.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableDiscoveryClient @EnableConfigServer public class ConfigServerMain3344 { public static void main(String[] args) { SpringApplication.run(ConfigServerMain3344.class, args); } }
4)创建配置文件,设置配置文件的地址等信息
server: port: 3344 spring: application: name: cloud-config-server cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} config: server: git: #github仓库上面的git仓库地址 uri: https://github.com/zhongyushi-git/spring-cloud-server-config.git #指定仓库的分支 default-label: master #拉取到本地的路径,最好是空目录,原因是在第一次启动时会清空指定目录 basedir: E:\config
5)启动项目,在浏览器访问http://localhost:3344/test-xxx.properties就可以看到配置的信息:
此时说明已完成从github上读取配置信息。
还可以使用http://localhost:3344/test-xxx.yml、http://localhost:3344/test-xxx.json查看配置信息。其配置读取规则如下:
/{application}/{profile}[/{label}] /{label}/{application}-{profile}.yml /{application}-{profile}.yml
而上面的test-xxx.yml等方式都是配置规则中的第三种,也就是说必须要指定一个文件的激活环境,如dev,prod等。如果有公共配置文件,那么在获取配置时会把公共配置追加到对应环境的配置中,就如application.yml和application-dev.yml的关系。
上述只是通过浏览器访问了一个配置文件内容。在实际中需要在项目中进行配置。
1.4客户端配置
1)新建maven子模块(cloud-provider8001),导入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--config客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> </dependencies>
主要是导入config的客户端。
2)新建启动类ProviderMain8001并添加注解
package com.zys.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ProviderMain8001 { public static void main(String[] args) { SpringApplication.run(ProviderMain8001.class, args); } }
3)配置application.yml,此文件只配置应用名称,这是用户级别的配置
spring:
application:
name: provider-client
4)配置bootstrap.yml,此文件指定配置服务的相关信息。这是系统基本的配置,优先级比application.yml更高
spring: cloud: config: #指定从github读取的配置文件的名称,不加后缀名 name: provider-client #配置中心服务路径 uri: http://localhost:3344 #指定访问的配置环境 profile: dev #指定仓库的分支 label: master
5)新建controller接口
package com.zys.cloud.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Value("${user.name}") private String name; @Value("${user.age}") private String age; @GetMapping("/user/get") public String get() { return "我是服务提供者 ,姓名:" + name + ",年龄" + age; } }
6)把配置中心的仓库代码拉取到本地,在里面添加两个文件,分别是provider-client.yml、provider-client-dev.yml。注意,这些文件必须以utf-8编码保存。
provider-client.yml内容如下,相当于原来的主配置文件,文件名需要和bootstrap.yml的名字一致:
spring: cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} user: name: 张三 age: 20
provider-client-dev.yml内容如下,可想而知,这个文件的dev环境的配置,同理还可以添加prod环境的配置,名字前缀必须和主配置文件一致。
server: port: 8001
7)启动服务提供者,访问http://localhost:8001/user/get,可以看到配置的信息:
自此就完成了配置。加入其它服务也是这样操作。
1.5动态刷新配置
1.5.1引言
虽然上面已完成配置,但先做一下下面的操作:
把provider-client.yml中user.name修改为李四并提交到远程仓库,再次调用http://localhost:8001/user/get,发现配置信息并未刷新。重启服务提供者后再访问,配置更新了,原因是在重启时更新了配置到本地,而只修改配置文件,配置并未更新到本地。有一种不重启服务的方法就是手动的刷新配置,步骤如下:
1)在服务提供者的bootstrap.yml中开启端口暴露
management: endpoints: web: exposure: include: "*"
2)在服务提供者的UserController类上添加注解@RefreshScope
添加完成后重启服务提供者
3)把provider-client.yml中user.name修改为王五并提交到远程仓库
4)使用postman发送一个post请求,手动更新
http://localhost:8021/actuator/refresh
再次访问http://localhost:8001/user/get,配置已更新。
从这里可以看出,并没有重启服务,只是手动发送了一条请求,就实现了动态的刷新。
2.bus(消息总线)
2.1定义
对于上面config的项目,我们只能通过手动的方式进行动态刷新配置,而如果同时修改了多个配置文件,那么就要进行多次的手动刷新,而bus可以实现一次刷新,在其他的服务中不需要再刷新就可生效。也就是说,它是一个把分布式分摊的节点与轻量级消息系统链接起来的框架,整合了java的事件处理机制和消息中间件的功能,目前仅支持RabbitMQ和Kafka。它能管理和传播分布式系统间的消息,可用于广播状态更改、事件推送等。
基本原理:ConfigClient实例都监听MQ中同一个topic。当一个服务刷新数据时,就会把这个信息放入topic中,这样其他监听同一topic的服务就能得到通知,然后去更新各自的配置。
设计思想:利用消息总线触发一个服务端ConfigServer的/bus/refresh端点从而来刷新客户端的配置。
2.2项目开发
本项目在上述项目的基础上进行开发。
2.2.1环境配置
在使用bus之前,必须先安装erlang和RabbitMQ。erlang的安装和RabbitMQ的安装请参考https://www.cnblogs.com/zys2019/p/12828152.html。
2.2.2 配置中心服务添加消息总线支持
1)添加依赖
<!--添加消息总线RbbitMQ支持--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
2)application.yml配置
spring: #rabbit服务配置 rabbitmq: host: localhost port: 5672 username: guest password: guest #rabbitmq暴露bus刷新配置的端点 management: #暴露bus刷新配置的端点 endpoints: web: exposure: #凡是暴露监控、刷新的都要有actuator依赖,bus-refresh就是actuator include: 'bus-refresh'
2.2.3客户端添加消息总线支持
1)添加依赖
<!--添加消息总线RbbitMQ支持--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
2)bootstrap.yml配置
spring: #rabbit服务配置 rabbitmq: host: localhost port: 5672 username: guest password: guest
开启端点暴露在前面已经配置,这里无需再次配置。
2.2.4 启动测试
1)先启动服务配置中心,然后启动服务提供者。访问http://localhost:8001/user/get,此时会看到原来的配置信息
2)将user.name再次修改,然后推送到github。不重启服务。再次访问返回的信息还是更新前的值。
3)使用postman发送一条post请求给服务3344去刷新配置中心服务
http://localhost:3344/actuator/bus-refresh
4)再访问http://localhost:8001/user/get,返回的信息已更新。这里并没有给服务提供者服务发送post请求,也实现了动态的刷新,这就是bus的广播。