前面我们已经学习了如何使用 RabbitMQ 消息队列,接着我们来简单介绍一下 SpringCloud 为我们提供的一些消息组件。
官方文档:https://docs.spring.io/spring-cloud-stream/docs/3.2.2/reference/html
前面我们介绍了 RabbitMQ,了解了消息队列相关的一些操作,但是可能我们会遇到不同的系统在用不同的消息队列,比如系统A 用的 Kafka、系统B 用的 RabbitMQ,但是我们现在又没有学习过 Kafka,那么怎么办呢?有没有一种方式像 JDBC 一样,我们只需要关心 SQL 和业务本身,而不用关心数据库的具体实现呢?
SpringCloud Stream 能够做到,它能够屏蔽底层实现,我们使用统一的消息队列操作方式就能操作多种不同类型的消息队列。
它屏蔽了 RabbitMQ 底层操作,让我们使用统一的 Input
和 Output
形式,以 Binder 为中间件,这样就算我们切换了不同的消息队列,也无需修改代码,而具体某种消息队列的底层实现是交给 Stream 在做的。
父工程作 SpringCloud 的版本管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>2021.0.1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
两个子项目添加 stream-rabbit 依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
回到目录…
①application.yml 配置文件
server:
port: 8080
spring:
cloud:
stream:
binders: #此处配置要绑定的rabbitmq的服务信息
rabbit-server: #绑定名称,随便起一个就行
type: rabbit #消息组件类型,这里使用的是RabbitMQ,就填写rabbit
environment: #服务器相关信息,按照下面的方式填写就行,爆红别管
spring:
rabbitmq:
host: 1.15.76.95
port: 5672
username: admin
password: 123456
virtual-host: /test
bindings:
test-out-0: #生产者是输出,默认名称为 方法名-out-index
destination: test.exchange # 指定交换机,没有会自动创建
②发送消息:StreamBridge.send(String bindingName, Object data)
@RestController
public class PublishController {
@Resource
StreamBridge bridge; //通过bridge来发送消息
@RequestMapping("/publish")
public String publish(){
//第一个参数是交换机名称,对应的不是在rabbitMQ中的名称,而是对于我们生产者来说的名称:<名称>-out-
//这里我们使用输出的方式,来将数据发送到消息队列,注意这里的名称会和之后的消费者Bean名称进行对应
bridge.send("test-out-0", "HelloWorld!");
return "消息发送成功!";
}
}
消息发送成功,我们来看看 RabbitMQ:新增了一个 test.exchange
交换机,是 topic
类型的。
但是目前还没有生成队列,所以此交换机没有绑定任何队列,我们刚刚发送的消息也就发丢了。
回到目录…
①application.yml 配置文件
server:
port: 8081
spring:
cloud:
stream:
binders:
rabbit-server:
type: rabbit
environment:
spring:
rabbitmq:
host: 1.15.76.95
port: 5672
username: admin
password: 123456
virtual-host: /test
bindings:
test-in-0: #其余配置不变,只需修改这里即可
destination: test.exchange
②接收消息:定义一个 java.util.function.Consumer
即可
@Component
public class ConsumerComponent {
@Bean("test") //注意这里需要对应到 test-out-0 和 test-in-0
public Consumer<String> consumer(){ // java.util.function.Consumer
return System.out::println;
}
}
我们也可以看到,test.exchange
交换机绑定到了这个队列,路由的 routingKey = #
(任意字符串)。
④再次测试:
这样,我们就通过使用 SpringCloud Stream 来屏蔽掉底层 RabbitMQ 来直接进行消息的操作了。
回到目录…
官方文档:https://cloud.spring.io/spring-cloud-bus/reference/html/
实际上它就相当于是一个消息总线,可用于向各个服务广播某些状态的更改(比如云端配置更改,可以结合 Config 组件实现动态更新配置,当然我们前面学习的 Nacos 其实已经包含这个功能了)或其他管理指令。
这里我们也是简单使用一下吧,Bus 需要基于一个具体的消息队列实现,比如 RabbitMQ 或是 Kafka,这里我们依然使用 RabbitMQ。
我们将最开始的微服务拆分项目继续使用,比如现在我们希望借阅服务的某个接口调用时,能够给用户服务和图书服务发送一个通知。
①父工程作 SpringCloud 的版本管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>2021.0.1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
②在 bookservice、userservice、borrowservice 三个服务中添加 bus-amqp 依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
③只需要在其中一个服务中添加此依赖即可,这里就选 borrowservice 吧
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
回到目录…
①在三个服务中进行 RabbitMQ 的相关信息配置:
spring:
rabbitmq:
addresses: 1.15.76.95
username: admin
password: 123456
virtual-host: /test
②仅在 borrowservice 中配置端口暴露:
management:
endpoints:
web:
exposure:
include: "*" #暴露端点,一会儿用于提醒刷新
然后启动我们的三个服务器,可以看到多了一个交换机:
该交换机绑定了三个服务各自的消息队列,这样就可以监听并接收到消息了:
现在我们访问一下 borrowservice 的接口:POST http://localhost:8082/actuator/busrefresh
此接口是用于通知别人进行刷新,可以看到调用之后,消息队列中成功出现了一次消费:
回到目录…
现在我们结合之前学习的 SpringCloud之Config配置中心,来看看是不是可以做到通知之后所有的配置动态刷新了。
①创建 config 服务端
a. 创建一个新的项目 config-server,并导入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
b. 写启动类,用 @EnableConfigServer
注解修饰:
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
c. 创建配置文件,并且添加本地仓库的一些信息:
server:
port: 8700
spring:
application:
name: configserver
cloud:
config:
server:
git:
# 这里填写的是本地仓库地址,远程仓库直接填写远程仓库地址 http://git...
uri: file://F:/Git/SpringCloudBus-Config
# 默认分支设定为你自己本地或是远程分支的名称
default-label: master
d. 使用 Git 指令创建本地仓库,并将配置文件提交到仓库中:
# 初始化仓库
git init
# 将配置文件添加标记
git add .
# 将标记的文件提交到本地仓库
git commit -m 'yml'
②客户端的配置
a. 我们的 bookservice 服务需要从服务器读取配置文件,首先需要添加依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
b. 编写 bootstrap.yml
(在 application.yml 加载之前,实现配置文件远程获取)
spring:
cloud:
config:
# 名称,其实就是文件名称
name: bookservice
# 配置服务器的地址
uri: http://localhost:8700
# 环境
profile: dev
# 分支
label: master
c. 编写一个测试的接口,查看是否能够实时刷新配置
@RefreshScope // 实现动态刷新
@RestController
public class BookController {
@Value("${test.value}")
private String val;
@GetMapping("/test")
public void test() {
System.out.println("test.value: " + val);
}
}
③测试
http://localhost:8081/test
,得到结果 test.value:3
/actuator/busrefresh [POST]
,通知其它服务刷新。http://localhost:8081/test
,得到结果 test.value:4
回到目录…
总结:
提示:这里对文章进行总结:
本文是对SpringCloud消息组件的学习,第一个组件是 SpringCloud Stream,实现消息队列的相关操作;第二个组件是 SpringCloud Bus,借助消息驱动来实现将消息广播到各个服务中,作为消息总线可实现服务配置动态更新。