菜鸟的springcloud学习总结(六):服务配置
- 说明
- 一、服务配置
- 二、Spring Cloud Config
- (1)Config服务端
- (2)仓库上配置文件命名及访问
- (3)Config客户端
- 三、Spring Cloud Bus
- (1)Config服务端
- (2)Config客户端
- 四、Spring Cloud Stream
- (1)生产者
- (2)消费者
说明
更新时间:2020/10/04 16:12,更新到了Spring Cloud Stream
本文主要对springcloud中的服务配置进行学习与记录,主要偏向于实战,本文会持续更新,不断地扩充
本文仅为记录学习轨迹,如有侵权,联系删除
一、服务配置
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。一个配置中心提供的核心功能,提供服务端和客户端支持,集中管理各环境的配置文件,配置文件修改之后,可以快速的生效,可以进行版本管理,支持大的并发查询,支持各种语言等
二、Spring Cloud Config
Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud使用git或svn存放配置文件,默认情况下使用git,我们先以git为例做一套示例。
下面采用git加码云的方式进行演示
前期准备
先在码云上面创建一个仓库,用于存放分布式的配置文件,并且创建对应的几个yml配置文件,config-dev.yml、config-pro.yml、config-test.yml分别表示开发环境、生产环境和测试环境的配置文件
(1)Config服务端
创建Config服务端对应的子模块cloud-config-server-3344,引入pom坐标依赖
<dependencies>
<!--引入公共包-->
<dependency>
<groupId>com.zsc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--spring cloud config 服务端包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--springboot starter启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
重点是spring cloud config 服务端包,然后配置yml文件
server:
port: 3344
spring:
application:
name: cloud-config-server3344
cloud:
config:
server:
git:
#码云上面的配置的仓库地址,公有的不用设置密码
uri: https://gitee.com/strong_rookie/spring-cloud-config.git
#搜索目录
search-paths:
- Spring Cloud Config
#读取分支
label: master
#注册进eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
主启动类,注意加上EnableConfigServer注解
之后就可以访问了,由于这里还注册了eureka,所以eureka服务注册中心也得打开
之后就可以访问了http://localhost:3344/master/config-pro.yml
(2)仓库上配置文件命名及访问
http请求地址和资源文件映射如下:
/{application}/{profile}[/{label}] |
/{application}-{profile}.yml |
/{label}/{application}-{profile}.yml |
/{application}-{profile}.properties |
/{label}/{application}-{profile}.properties |
仓库上的配置文件可以按照上面的任一方式命名,上面的项目用的是第二种命名方式,例如:config-dev.yml
上面的命名方式决定了路径的访问方式,例如:config-dev.yml,访问时则是http://localhost:3344/master/config-dev.yml路径
(3)Config客户端
创建Config子模块cloud-config-client-3355,引入依赖
<dependencies>
<!--引入公共包-->
<dependency>
<groupId>com.zsc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- spring cloud config 客户端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--springboot starter启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
重点是spring cloud config 客户端包,然后创建配置文件bootstrap.yml
server:
port: 3355
spring:
application:
name: cloud-config-client3355
cloud:
config:
label: master #分支名称
name: config #配置文件的名称
profile: dev #读取后缀名称
uri: http://localhost:3344/ #配置config中心地址
#上面的配置解释为:master分支上config-dev.yml的配置文件被读取,http://localhost:3344/master/config-dev.yml
#注册进eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
#暴露监控端点,实现配置文件实时更新
management:
endpoints:
web:
exposure:
include: "*"
这里配置了配置文件实时更新,这样的话假设仓库上的配置文件修改了,也能实现实时的更新,当然,这之间还有一个小步骤
关于bootstrap.yml配置文件
然后是主启动类
然后是业务类,客户端通过接口的方式的将获取配置文件的内容,就是以rest风格将配置对外暴露
代码
@RestController
@RefreshScope//实现配置文件的实时更新
public class ConfigClientController {
@Value("${server.version}")
private String serverVersion;
@GetMapping("/getServerVersion")
public String getServerVersion(){
return serverVersion;
}
}
注意controller里面的类要实现实时的更新配置文件需要在每一个controller类上加一个刷新的注解
启动项目进行测试
访问接口http://localhost:3355/getServerVersion,可以获取配置文件的内容
如何实现实时更新配置文件内容,首先在远程仓库更新一下配置文件
再访问接口访问接口http://localhost:3355/getServerVersion发现还是version2.0,这是因为还有一个步骤,就是需要先发送一个post请求如下,用于提醒客户端我已经更新了配置文件
代码
curl -X POST "http://localhost:3355/actuator/refresh"
再访问接口http://localhost:3355/getServerVersion
当然这种是半自动化配置,虽然当修改完配置文件后,可以用脚本实现自动发送post以达到实时更新的目的
三、Spring Cloud Bus
上面的项目实现了客户端的配置的刷新,但是还有问题,当客户端越来越多的情况下,远程仓库上的配置改变后,客户端要得到最新的配置文件内容,则需要每一个客户端都要发送一个post请求
这样的话,当有三四十个客户端的情况下,就会显得很麻烦,所以下面就有Spring Cloud Bus消息总线,用来实现一次广播,实现所有的客户端刷新
简单介绍Spring Cloud Bus
SpringCloud Bus使用轻量级消息代理将分布式系统的节点连接起来。然后可以使用此代理广播状态更改(例如配置更改)或其他管理指令。本文结合RabbitMQ+码云实现上面Config配置中心的自动刷新。
它的广播方式有两种,这里主要采用下面的广播方式
远程仓库上的配置文件修改之后,首先由服务端Config服务端发送一个post请求,Spring Cloud Bus接收到后,通过广播的方式将消息发送给所有的Config客户端,客户端实现配置文件的刷新为什么被称为总线?
实战
下面直接开始实战,为了能看到广播的效果,必须要再创建一个Config客户端cloud-config-client-3366,内容基本跟cloud-config-client-3355一样,这里还需要先安装mq消息队列,并且可以访问
(1)Config服务端
在cloud-config-server-3344引入对应的坐标依赖
<!-- 添加消息总线RabbitMQ支持 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
修改配置文件,主要增加了mq的配置和bus刷新配置的端点
代码如下
server:
port: 3344
spring:
application:
name: cloud-config-server3344
cloud:
config:
server:
git:
#码云上面的配置的仓库地址,公有的不用设置密码
uri: https://gitee.com/strong_rookie/spring-cloud-config.git
#搜索目录
search-paths:
- Spring Cloud Config
#读取分支
label: master
#添加mq配置
rabbitmq:
host: 39.96.22.34
port: 5675
username: guest
password: guest
virtual-host: /
#mq相关配置,暴露bus刷新配置的端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
#注册进eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
(2)Config客户端
客户端的配置文件修改,以3355项目为例,主要增加下面的坐标
代码
<!-- 添加消息总线RabbitMQ支持 -->
<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>
修改配置文件
代码
server:
port: 3355
spring:
application:
name: cloud-config-client3355
cloud:
config:
label: master #分支名称
name: config #配置文件的名称
profile: dev #读取后缀名称
uri: http://localhost:3344/ #配置config中心地址
#上面的配置解释为:master分支上config-dev.yml的配置文件被读取,http://localhost:3344/master/config-dev.yml
#添加mq配置
rabbitmq:
host: 39.96.22.34
port: 5675
username: guest
password: guest
virtual-host: /
#注册进eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
#暴露监控端点,实现配置文件实时更新
management:
endpoints:
web:
exposure:
include: "*"
注意:对3366项目也做同样的操作
启动项目
访问3355和3366项目,两个项目获取到的配置文件数据都是version3.0
现在在远程仓库上修改配置文件,改为4.0
此时所有的服务端和客户端还没有刷新,访问3355和3366项目接口,仍然是3.0
此时只需要Config服务端发送一个post请求即可通过广播的方式对所有的客户端进行刷新
代码
curl -X POST "http://localhost:3344/actuator/bus-refresh"
下面重新访问3355和3366项目,发现两个都进行了更新
此外还可以对某一个客户端进行更新,另一个则不更新,重新修改仓库上的配置文件为5.0
现在更新3355的配置,3366不更新
代码
curl -X POST "http://localhost:3344/actuator/bus-refresh/cloud-config-client3355:3355"
参数:
cloud-config-client3355:3355: 是3355项目yml配置文件中的spring.application.name,3355为对应端口
四、Spring Cloud Stream
简介
简单理解一下定义,Spring Cloud Stream 是一个构建消息驱动微服务的框架。应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们配置来 binding ,而 Spring Cloud Stream 的 binder 负责与消息中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。
通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。目前仅支持RabbitMQ、Kafka。
为什么要用Srpring Cloud Stream
上面的项目已经实现了通过广播的方式实现一个post更新所有的客户端的配置更新功能,但是有一种情况下,假设一个项目里面用很多个微服务,里面用到的消息中间件既有mq,又有kafka,一个项目中有多个消息中间件,对于程序员,因为人员都不友好,这个时候就需要用到Spring Cloud Stream,它就类似jpa,屏蔽底层消息中间件的差异,程序员主要操作Spring Cloud Stream即可,而不需要管底层是kafka还是rabbitMq。
官方架构图
常用注解和api
组成 | 说明 |
Middleware | 中间件,目前只支持RabbitMQ和Kafka |
Binder | Binder是应用与消息中间件之间的封装,目前实行了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现 |
@Input | 注解标识输入通道,通过该输入通道接收到的消息进入应用程序 |
@Output | 注解标识输出通道,发布的消息将通过该通道离开应用程序 |
@StreamListener | 监听队列,用于消费者的队列的消息接收 |
@EnableBinding | 指信道channel和exchange绑定在一起 |
Spring Cloud Stream的业务流程
下面开始实战
(1)生产者
创建生产者cloud-stream-rabbitmq-provider8801,引入pom,这里只是使用Stream,所以不引入Eureka和Actutor
<dependencies>
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--stream rabbit -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置yml
代码
server:
port: 8801
spring:
application:
name: cloud-stream-rabbitmq-provider8801
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: # 表示定义的名称。用于binding整合
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关环境配置
spring:
rabbitmq:
host: 39.96.22.34
port: 5675
username: guest
password: guest
bindings: #服务的整合过程
output: #这个名字是一个通道的名称
destination: studyExchange #表示要使用的Exchange名称定义
content-type: application/json #设置消息类型,
binder: defaultRabbit #设置要绑定的消息服务的具体设置
主启动类
服务层创建生产者
接口
public interface IMessageProvider {
public String send();
}
对应实现类
@EnableBinding(Source.class)
public class IMessageProviderImpl implements IMessageProvider {
@Resource
private MessageChannel output;
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
System.out.println("**********************serial = " + serial);
return null;
}
}
controller接口
@RestController
public class SendMessageController {
@Resource
private IMessageProvider messageProvider;
@GetMapping("/sendMessage")
public String sendMessage(){
return messageProvider.send();
}
}
生产者创建成功
(2)消费者
创建消费者模块1cloud-stream-rabbitmq-consumer8802,引入pom文件
<dependencies>
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--stream rabbit -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
跟生产者几乎一样的pom,配置yml
server:
port: 8802
spring:
application:
name: cloud-stream-rabbitmq-consumer8802
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: # 表示定义的名称。用于binding整合
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关环境配置
spring:
rabbitmq:
host: 39.96.22.34
port: 5675
username: guest
password: guest
bindings: #服务的整合过程
input: #这个名字是一个通道的名称2
destination: studyExchange #表示要使用的Exchange名称定义,指定使用哪个Exchange
content-type: application/json #设置消息类型,
binder: defaultRabbit #设置要绑定的消息服务的具体设置
group: zsc8802 #分组
主启动类
服务层创建消费者
代码
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListener {
@Value("${server.port}")
private String port;
@StreamListener(Sink.INPUT)
public void input(Message<String> message){
System.out.println("消费者1号,---->接收到的消息:" + message.getPayload()+"\t port:"+port);
}
}
创建完再创建一个消费者模块2cloud-stream-rabbitmq-consumer8803,步骤和配置跟上面一样,创建完启动这个3个项目
查看rabbitmq
点进对应的交换机,发现里面有俩个用户,一个属于zsc8802分组,一个属于zsc8803分组
此时发送8801生产者发送两条消息,两个消费者均消费了两条消息
如果将8802和8803项目分组分成同一个组,那么两条消息,会采用轮询的方式一人消费一条
重启项目,生成者再发送两条消息,则会一人一条