1.分布式配置中心介绍
1.1 什么是分布式配置中心
在常规的开发中,每个微服务都包含代码和配置。其配置包含服务配置、各类开关和业务配置。如果系统结构中的微服务节点较少,那么常规的代码+配置的开发方式足以解决问题。当系统逐步迭代,其微服务会越来越复杂,慢慢演化成网状依赖结构,这个时候常规的代码+配置的开发方式就并不合适了,因为还要考虑整体系统的扩展性、伸缩性和耦合性等。这些问题中,配置的管理也是非常麻烦的。
- 如果还是以常规开发形式管理配置,则要承担反复修改编译代码、重启系统、重新打包等风险。所以,一个可以集中管理,带有版本控制的配置中心应运而生。
spring cloud config 就是一个配置中心。其采用集中式管理每个微服务的配置信息,并使用 GIT 等版本仓库统一存储配置内容,实现版本化管理控制。微服务与配置中心使用 rest 方式交互来实现可扩展的配置服务
spring cloud config 配置中心解决了微服务系统的配置中心化、配置版本控制、平台独立、语言独立等问题,其特性如下:
1.提供服务端和客户端支持(spring cloud config server 和 spring cloud config client)
2.集中式管理分布式环境中的配置信息
3.基于 spring 环境提供配置管理,与 spring 系列框架无缝结合
4.可用于任何语言开发环境
5.默认基于 GIT 仓库实现版本控制
1.2 分布式配置中心结构图
2.基于 Gitee 管理配置文件
- 在 Gitee 中集中管理配置文件,为保证文件集中化管理,独立创建仓库。具体过程如下:
2.1 创建仓库
2.2 创建文件并编辑内容
2.2.1 application-service-config.yml 配置文件
2.2.2 application-service-config-dev.yml 配置文件
3.搭建分布式配置中心服务端
3.1 POM 依赖
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>com.csdn</groupId>
<artifactId>configserver</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
</dependencies>
</project>
3.2 配置文件 application.yml
spring:
application:
name: config-server
cloud: # 配置 Spring Cloud Config 服务端相关信息
config:
server:
git:
uri: https://gitee.com/csdn_test/cloudconfig.git # 访问的远程仓库 uri
username: csdn_test # Gitee 用户名
password: enter_your_password # Gitee 密码
server:
port: 8888 # Spring Cloud Config 服务端默认端口为 8888,可以自定义修改。此端口为 Spring Cloud Config 客户端默认访问端口。
3.3 启动类
package com.csdn;
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 - 开启配置中心服务端。
//应用会根据全局配置文件访问GIT远程仓库,并将远程仓库中的配置内容下载到本地
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApp {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApp.class, args);
}
}
3.4 浏览器测试获取 Gitee 中的配置文件
- 当配置中心服务端启动后,可以通过浏览器访问配置中心服务端应用,获取我们保存在 Gitee 中的配置文件内容,具体访问路径格式如下:
- http://config-server-ip:port/name/profile/label
- name - Gitee 中保存的配置文件核心命名
- profile - Gitee 中保存的配置文件扩展环境命名
- label - Gitee 中的仓库分支
- 如访问: http://localhost:8888/application-service-config/default/master
- 代表在 Gitee 中 master 分支下 , 查找命名为 application-service-config.yml | application-service-config.properties 配置文件。
如访问: http://localhost:8888/application-service-config/dev/master
代表在 Gitee 中 master 分支下 , 查找命名为 application-service-config-dev.yml | application-service-config-dev.properties 配置文件。
4.搭建分布式配置中心客户端
- 每个 Spring Cloud 微服务应用(除 Eureka 和 Config 配置中心服务端)都可以看做是 Spring Cloud Config 客户端应用,都可以通过 Spring Cloud Config 服务端动态获取保存在 Gitee 中的配置文件
4.1 POM 依赖
<?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>cloudhystrix</artifactId>
<groupId>com.csdn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>applicationservice</artifactId>
<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-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
</project>
4.2 配置文件 bootstrap.yml
- 使用优先级最高的全局配置文件 bootstrap.properties 来配置当前应用启动时访问哪一个分布式配置中心服务端获取配置文件。
spring:
cloud:
config:
uri: http://localhost:8888/ # 分布式配置中心服务端访问地址,默认为 http://localhost:8888/
name: application-service-config # 要读取的配置文件主体命名
profile: default # 要读取的配置文件扩展环境名, 默认 default
label: master # 在 Gitee 仓库的哪一个分支中读取配置文件, 默认 null, 即在 master 分支中读取
1234567
5.配置热刷新
- 通过 spring-boot-starter-actuator 可以实现配置中心客户端应用不重启,当某事件触发时,重新访问配置中心服务端,获取新的配置文件,并重新初始化本地环境。这种不重启应用实现环境刷新的方式称为热刷新。
- 具体实现过程如下:
5.1 POM 依赖
<?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>cloudhystrix</artifactId>
<groupId>com.csdn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>applicationservice</artifactId>
<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-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
5.2 配置文件
spring:
cloud:
config:
uri: http://localhost:8888/ # 分布式配置中心服务端访问地址,默认为 http://localhost:8888/
name: application-service-config # 要读取的配置文件主体命名
profile: default # 要读取的配置文件扩展环境名, 默认 default
label: master # 在 Gitee 仓库的哪一个分支中读取配置文件, 默认 null, 即在 master 分支中读取
management:
endpoints:
web:
exposure:
include:
- refresh # 开启热刷新服务, 也可以在 Gitee 中的配置文件内定义。
- info
- health
5.3 测试热刷新环境
- 发送 POST 请求到 http://ip:port/actuator/refresh,应用会重新访问配置中心服务端,加载最新的配置文件内容,并刷新当前应用环境。
5.4 代码中使用远程配置内容
- 如果代码中使用了 Gitee 中保存的配置文件内容,那么希望热刷新的时候,重新初始化对象属性值,则需要在对应类型上增加注解@RefreshScope,通知 Spring 容器,当前类型的对象,在环境刷新时,需要重新初始化对象。
package com.csdn.controller;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ServiceController {
@Value("${test.str}")
private String str;
@PostMapping("/testPost")
public Object testPost(){
System.out.println("testPost method run");
return "写操作返回";
}
@GetMapping("/testGet")
public Object testGet(){
System.out.println("testGet method run");
return "读操作返回";
}
@GetMapping
public Object first(){
System.out.println("run");
try {
// 用于模拟远程服务调用延时
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "测试 Spring Cloud Netflix Ribbon 开发服务提供者";
}
}
6 Bus 消息总线
远程的配置文件更新了,运维只需要发一个请求,所有用到这个配置文件的几百个应用更新了。
6.1 Bus 概述
Spring Cloud Bus 是用轻量的消息中间件将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。关键的思想就是,消息总线可以为微服务做监控,也可以实现应用程序之间相通信。
Spring Cloud Bus 可选的消息中间件包括 RabbitMQ 和 Kafka 。
6.2 RabbitMQ 回顾
message queue
RabbitMQ 提供了 6 种工作模式:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing
路由模式、Topics 主题模式、RPC 远程调用模式(远程调用,不太算 MQ;暂不作介绍)。
6.3 Bus 快速入门-运维
-
分别在 config-server 和 config-client中引入 bus依赖:bus-amqp
<!-- bus --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
-
分别在 config-server 和 config-client中配置 RabbitMQ
server 的配置文件
server: port: 9527 spring: application: name: config-server # spring cloud config cloud: config: server: # git 的 远程仓库地址 git: uri: https://gitee.com/itheima_cch/itheima-configs.git label: master # 分支配置 #配置rabbitmq信息 rabbitmq: host: localhost port: 5672 username: guest password: guest virtual-host: / # 将自己注册到eureka中 eureka: client: service-url: defaultZone: http://localhost:8761/eureka # 暴露bus的刷新端点 management: endpoints: web: exposure: include: 'bus-refresh'
client的配置文件 bootstrap.yml
# 配置config-server地址 # 配置获得配置文件的名称等信息 spring: cloud: config: # 配置config-server地址 # uri: http://localhost:9527 # 配置获得配置文件的名称等信息 name: config # 文件名 profile: dev # profile指定, config-dev.yml label: master # 分支 # 从注册中心去寻找config-server地址 discovery: enabled: true service-id: CONFIG-SERVER #配置rabbitmq信息 rabbitmq: host: localhost port: 5672 username: guest password: guest virtual-host: / management: endpoints: web: exposure: include: '*'
-
在config-server中设置暴露监控断点:bus-refresh
# 暴露bus的刷新端点 management: endpoints: web: exposure: include: 'bus-refresh'
-
启动测试
往配置中心发请求
curl -X POST http://localhost:9527/actuator/bus-refresh
7 Stream 消息驱动
7.1 Stream 概述
也就是无需管mq的种类是什么,只需写业务逻辑即可,一套代码mq随便换,和jdbc原理差不多统一的一个管理。
rabbitTemplate.convertAndSend(“交换机”,luyoukey,“消息体”)
kafka.send()
1Spring Cloud Stream 是一个构建消息驱动微服务应用的框架。
2Stream 解决了开发人员无感知的使用消息中间件的问题,因为Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件,使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。
3Spring Cloud Stream目前支持两种消息中间件RabbitMQ和Kafka
Stream 组件
Spring Cloud Stream 构建的应用程序与消息中间件之间是通过绑定器 Binder 相关联的。绑定器对于应用程序而言起到了隔离作用, 它使得不同消息中间件的实现细节对应用程序来说是透明的。
binding 是我们通过配置把应用和spring cloud stream 的 binder 绑定在一起
output:发送消息 Channel,内置 Source接口
input:接收消息 Channel,内置 Sink接口
7.2 Stream 消息生产者 stream-producer
pom
<?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>stream-parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>stream-producer</artifactId>
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies>
</project>
ProducerApp
package com.itheima.stream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class,args);
}
}
application.yml
server:
port: 8000
spring:
cloud:
stream:
# 定义绑定器,绑定到哪个消息中间件上
binders:
itheima_binder: # 自定义的绑定器名称
type: rabbit # 绑定器类型
environment: # 指定mq的环境
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
bindings:
output: # channel名称
binder: itheima_binder #指定使用哪一个binder
destination: itheima_exchange # 消息目的地
消息发送类 MessageProducer
package com.itheima.stream.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
@Component
@EnableBinding(Source.class)
public class MessageProducer {
@Autowired
private MessageChannel output;
public void send(){
String msessage = "hello stream~~~";
//发送消息
output.send(MessageBuilder.withPayload(msessage).build());
System.out.println("消息发送成功~~~");
}
}
调用的controller ProducerController
package com.itheima.stream.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProducerController {
@Autowired
private MessageProducer producer;
@RequestMapping("/send")
public String sendMsg(){
producer.send();
return "success";
}
}
7.3 Stream 消息消费者 stream-consumer
pom
<?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>stream-parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>stream-consumer</artifactId>
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies>
</project>
ConsumerApp
package com.itheima.stream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
application.yml
server:
port: 9000
spring:
cloud:
stream:
# 定义绑定器,绑定到哪个消息中间件上
binders:
itheima_binder: # 自定义的绑定器名称
type: rabbit # 绑定器类型
environment: # 指定mq的环境
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
bindings:
input: # channel名称
binder: itheima_binder #指定使用哪一个binder
destination: itheima_exchange # 消息目的地
消息接收类 MessageListener
package com.itheima.stream.consumer;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
/**
* 消息接收类
*/
@EnableBinding({Sink.class})
@Component
public class MessageListener {
@StreamListener(Sink.INPUT)
public void receive(Message message){
System.out.println(message);
System.out.println(message.getPayload());
}
}