74.Config分布式配置中心介绍
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

是什么
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
怎么玩
SpringCloud Config分为服务端和客户端两部分。
服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
能干嘛
与GitHub整合配置
75.Config配置总控中心搭建(这里我用了gitee)
用你自己的账号在GitHub上新建一个名为springcloud-config的新Repository
由上一步获得刚新建的git地址 - https://gitee.com/yang-xiao-bai/springcloud-config.git这里用ssh可能会报错,踩了一个坑
文件内容见上面地址
客户端配置
1.建Module模块cloud-config-center-3344
2.pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| <?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>springcloud</artifactId> <groupId>com.yxz.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-center-3344</artifactId>
<dependencies> <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.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.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
</project>
|
3.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| server: port: 3344
spring: application: name: cloud-config-center cloud: config: server: git: uri: https://gitee.com/yang-xiao-bai/springcloud-config.git search-paths: - springcloud-config username: gitee的账号 password: gitee的密码 label: master
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
|
4.主启动
1 2 3 4 5 6 7
| @SpringBootApplication @EnableConfigServer public class ConfigCenterMain3344 { public static void main(String[] args) { SpringApplication.run(ConfigCenterMain3344.class, args); } }
|
配置读取规则
/{label}/{application}-{profile}.yml(推荐)
/{application}-{profile}.yml
/{application}/{profile}[/{label}]
成功实现了用SpringCloud Config通过GitHub获取配置信息
76.Config客户端配置与测试
新建cloud-config-client-3355
POM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>springcloud</artifactId> <groupId>com.yxz.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-client-3355</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</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> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| server: port: 3355
spring: application: name: config-client cloud: config: label: master name: config profile: dev uri: http://localhost:3344
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
management: endpoints: web: exposure: include: "*"
|
applicaiton.yml是用户级的资源配置项
bootstrap.yml是系统级的,优先级更加高
Bootstrap
属性有高优先级,默认情况下,它们不会被本地配置覆盖。 Bootstrap context
和Application Context
有着不同的约定,所以新增了一个bootstrap.yml
文件,保证Bootstrap Context
和Application Context
配置的分离。
主启动
1 2 3 4 5 6 7
| @SpringBootApplication @EnableEurekaClient public class ConfigClientMain3355 { public static void main(String[] args) { SpringApplication.run(ConfigClientMain3355.class, args); } }
|
业务类
1 2 3 4 5 6 7 8 9 10 11 12 13
| @RestController @RefreshScope public class ConfigClientController { @Value("${config.info}") private String configInfo;
@GetMapping("/configInfo") public String getConfigInfo() { return configInfo; }
}
|
动Config配置中心3344微服务并自测
启动3355作为Client准备访问
成功访问
在gitee中改config-dev.yml配置,比如更改版本号version
77.Config动态刷新之手动版
动态刷新步骤:
修改3355模块
POM引入actuator监控
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
|
修改YML,添加暴露监控端口配置:
1 2 3 4 5 6
| management: endpoints: web: exposure: include: "*"
|
@RefreshScope业务类Controller修改
1 2 3 4 5 6
| @RestController @RefreshScope public class ConfigClientController { ... }
|
测试
此时修改gitee配置文件内容 -> 访问3344 -> 访问3355
http://localhost:3355/configInfo 还是没有改变
需要运维人员发送Post请求刷新3355
1 2
| curl -X POST "http://localhost:3355/actuator/refresh"
|

再次测试
http://localhost:3355/configInfo
3355改变没有??? 改了。
想想还有什么问题?
78.Bus消息总线是什么
上—讲解的加深和扩充
Spring Cloud Bus配合Spring Cloud Config使用可以实现配置的动态刷新。
是什么
它整合了Java的事件处理机制和消息中间件的功能。
Spring Clud Bus目前支持RabbitMQ和Kafka。

基本原理
ConfigClient实例都监听MQ中同一个topic(默认是springCloudBus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一Topic的服务就能得到通知,然后去更新自身的配置。
79.Bus之RabbitMQ环境配置
安装rabbitmq,我的安装在虚拟机
80.Bus动态刷新全局广播的设计思想和选型
1.新建cloud-config-client-3366
同3355
设计思想
1)利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置

2)利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置

图二更合适
81.Bus动态刷新全局广播配置实现
给cloud-config-center-3344配置中心服务端添加消息总线支持
pom
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
|
yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| server: port: 3344
spring: application: name: cloud-config-center cloud: config: server: git: uri: https://gitee.com/yang-xiao-bai/springcloud-config.git search-paths: - springcloud-config username: gitee账号 password: gitee密码 label: master rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
management: endpoints: web: exposure: include: 'bus-refresh'
|
给cloud-config-client-3355客户端添加消息总线支持 和 给cloud-config-client-3366客户端添加消息总线支持
pom
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
|
yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| server: port: 3355
spring: application: name: config-client cloud: config: label: master name: config profile: dev uri: http://localhost:3344
rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
management: endpoints: web: exposure: include: "*"
|
测试
修改Github上配置文件增加版本号

一次发送,处处生效
82.Bus动态刷新定点通知
不想全部通知,只想定点通知
简单一句话 - 指定具体某一个实例生效而不是全部

83.Stream为什么被引入
常见MQ(消息中间件):
ActiveMQ
RabbitMQ
RocketMQ
Kafka
有没有一种新的技术诞生,让我们不再关注具体MQ的细节,我们只需要用一种适配绑定的方式,自动的给我们在各种MQ内切换。
84.Stream是什么及Binder介绍
官方文档1
官方文档2
Cloud Stream中文指导手册
目前仅支持RabbitMQ、 Kafka。
Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
85.Stream的设计思想

为什么用Cloud Stream?
解耦合
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。
Binder:

Stream中的消息通信方式遵循了发布-订阅模式
Topic主题进行广播
在RabbitMQ就是Exchange
在Kakfa中就是Topic
86.Stream编码常用注解简介
Spring Cloud Stream标准流程套路

组成 |
说明 |
Middleware |
中间件,目前只支持RabbitMQ和Kafka |
Binder |
inder是应用与消息中间件之间的封装,目前实行了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现 |
@Input |
注解标识输入通道,通过该输乎通道接收到的消息进入应用程序 |
@Output |
注解标识输出通道,发布的消息将通过该通道离开应用程序 |
@StreamListener |
监听队列,用于消费者的队列的消息接收 |
@EnableBinding |
指信道channel和exchange绑定在一起 |
案例说明
工程中新建三个子模块
cloud-stream-rabbitmq-provider8801,作为生产者进行发消息模块
cloud-stream-rabbitmq-consumer8802,作为消息接收模块
cloud-stream-rabbitmq-consumer8803,作为消息接收模块
87.Stream消息驱动之生产者
新建Module:cloud-stream-rabbitmq-provider8801
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>springcloud</artifactId> <groupId>com.yxz.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>cloud-stream-rabbitmq-provider8801</artifactId> <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-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
</project>
|
yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| server: port: 8801 spring: application: name: cloud-stream-provider rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root cloud: stream: binders: defaultRabbit: type: rabbit environment: spring: rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root bindings: output: destination: studyExchange content-type: application/json binder: defaultRabbit
eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 lease-expiration-duration-in-seconds: 5 instance-id: send-8801.com prefer-ip-address: true
|
如果报错拒绝连接,在spring下面添加这个,就好了
1 2 3 4 5
| rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root
|
启动类
业务类
service接口
1 2 3
| public interface IMessageProvider { public String send(); }
|
serviceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @EnableBinding(Source.class) public class MessageProviderImpl implements IMessageProvider {
@Resource private MessageChannel output;
@Override public String send() { String serial = UUID.randomUUID().toString(); this.output.send(MessageBuilder.withPayload(serial).build()); System.out.println("***serial: "+serial); return serial; } }
|
controller
1 2 3 4 5 6 7 8 9 10 11 12
| @RestController public class SendMessageController { @Resource private IMessageProvider messageProvider;
@GetMapping(value = "/sendMessage") public String sendMessage() { return messageProvider.send(); } }
|
测试
成功
88.Stream消息驱动之消费者
建Module:cloud-stream-rabbitmq-consumer8802
pom同上
yml,报错同上,加上一个配置就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| server: port: 8802
spring: application: name: cloud-stream-consumer rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root cloud: stream: binders: defaultRabbit: type: rabbit environment: spring: rabbitmq: host: 192.168.20.128 port: 5672 username: root password: root bindings: input: destination: studyExchange content-type: application/json binder: defaultRabbit
eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 lease-expiration-duration-in-seconds: 5 instance-id: receive-8802.com prefer-ip-address: true
|
测试
启动EurekaMain7001
启动StreamMQMain8801
启动StreamMQMain8802
8801发送8802接收消息
89.Stream之消息重复消费
克隆出来一份运行8803 - cloud-stream-rabbitmq-consumer8803
启动
RabbitMQ
服务注册 - 8801
消息生产 - 8801
消息消费 - 8802
消息消费 - 8802
运行后有两个问题
有重复消费问题
消息持久化问题
消费
生产实际案例
订单系统我们做集群部署,都会从RabbitMQ中获取订单信息,那如果一个订单同时被两个服务获取到,那么就会造成数据错误,我们得避免这种情况。这时我们就可以使用Stream中的消息分组来解决。
同组内的属于竞争关系

90.Stream之group解决消息重复消费
原理
微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。
不同的组是可以重复消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
8802/8803都变成不同组,group两个不同
group: A_Group、B_Group
8802修改YML,group为yxz1
8803修改YML,group为yxz2
发现消息8002和8003都接收到了
8802/8803都变成同组,group两个相同
group: A_Group
8802修改YML,group为yxz1
8803修改YML,group为yxz1
发现消息8002和8003轮流接到消息
91.Stream之消息持久化
一句话,配置了group的分组,实现了持久化,宕机重启后可以收到积压的消息
没配置group的不行
92.Sleuth是什么
为什么会出现这个技术?要解决哪些问题?
在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败。
是什么
一句话:可以监控每次调用从头到尾调用的接口和时间
93.Sleuth之zipkin搭建安装
下载新地址
1
| java -jar zipkin-server-2.12.9-exec.jar
|
运行控制台
http://localhost:9411/zipkin/
94.Sleuth链路监控展现
1服务提供者
cloud-provider-payment8001
pom
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10
| spring: application: name: cloud-payment-service
zipkin: base-url: http://localhost:9411 sleuth: sampler: probability: 1
|
controller
1 2 3 4
| @GetMapping("/payment/zipkin") public String paymentZipkin() { return "hi ,i'am paymentzipkin server fall back,welcome to here, O(∩_∩)O哈哈~"; }
|
2服务消费者
cloud-provider-payment80
pom
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10
| spring: application: name: cloud-payment-service
zipkin: base-url: http://localhost:9411 sleuth: sampler: probability: 1
|
业务类OrderController
1 2 3 4 5 6 7 8
| @GetMapping("/consumer/payment/zipkin") public String paymentZipkin() { String result = restTemplate.getForObject("http://localhost:8001"+"/payment/zipkin/", String.class); return result; }
|
4.依次启动eureka7001/8001/80 - 80调用8001几次测试下
5.打开浏览器访问: http://localhost:9411
