前言
前面的章节我们讲了Spring Cloud Gateway
本节,继续微服务专题的内容分享,共计16小节,分别是:
- 微服务专题01-Spring Application
- 微服务专题02-Spring Web MVC 视图技术
- 微服务专题03-REST
- 微服务专题04-Spring WebFlux 原理
- 微服务专题05-Spring WebFlux 运用
- 微服务专题06-云原生应用(Cloud Native Applications)
- 微服务专题07-Spring Cloud 配置管理
- 微服务专题08-Spring Cloud 服务发现
- 微服务专题09-Spring Cloud 负载均衡
- 微服务专题10-Spring Cloud 服务熔断
- 微服务专题11-Spring Cloud 服务调用
- 微服务专题12-Spring Cloud Gateway
- 微服务专题13-Spring Cloud Stream (上)
- 微服务专题14-Spring Cloud Bus
- 微服务专题15-Spring Cloud Stream 实现
- 微服务专题16-Spring Cloud 整体回顾
本节内容重点为:
- RabbitMQ
- Spring RabbitMQ
- Spring Boot RabbitMQ
Spring Cloud Stream 相关技术
Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架。它可以基于 Spring Boot 来创建独立的、可用于生产的 Spring 应用程序。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并引入了发布-订阅、消费组、分区这三个核心概念。通过使用 Spring Cloud Stream,可以有效简化开发人员对消息中间件的使用复杂度,让系统开发人员可以有更多的精力关注于核心业务逻辑的处理。
以Java8 Stream流为例:
Stream
.of(1, 2, 3, 4, 5) // 生产
.map(String::valueOf) // 处理
.forEach(System.out::println); // 消费
这里一共出现了三种身份:
-
Publisher
:生产者 -
Subscriber
:处理者 -
Processor
:消费者
元编程:就是基于编程的编程、比如像反射Reflection、函数式编程Function、Lambda表达式、表达式语言即${user.age}
Spring Cloud Data Flow
区别于,上面提到的steam流处理, Spring Cloud Data Flow以应用为单位,实现生产,处理,消费这三个过程的!
每个进程之间应该有这不同的进程结束号,使用System.exit(N) ,N值为可变整数,加以区分不同进程。
Spring Cloud Stream 整合 RabbitMQ
我们这里使用本专题之前已经搭建好的项目:
- spring-cloud-client-application
- spring-cloud-server-application
在此基础上对RabbitMQ做技术实现:
1、pom依赖配置。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2、 配置项。
我们参考源码:org.springframework.cloud.stream.binder.rabbit.properties.RabbitBindingProperties
不难发现,通过以下配置实现SpringCloud与rabbitmq 的双向绑定:
## IP、端口、用户名、密码、虚拟主机
## 默认使用 / 的vhost,如果修改vhost,加在端口后即可,如 /testhost
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
# Spring Cloud Stream 配置
## 驱动的名字是 test2020
## spring.cloud.stream.bindings.${channel-name}.destination
## destination = topic
spring.cloud.stream.bindings.test2020.binder = rabbit
spring.cloud.stream.bindings.test2020.destination = test2020
小技巧,学习语言,要掌握一通百通的道理,源码设计者也是通过前人不断总结的经验不断地完善模型,比如Spring 中各种 xxxTemplate 实现 的xxxOperations就是如此:
- JdbcTemplate:Spring 操作 jdbc连接数据库。
- RedisTemplate:Spring操作redis连接缓存数据库。
- RabbitTemplate:Spring操作rabbit连接消息中间件。
- KafkaTemplate:Spring操作kafka连接消息中间件。
- RestTemplate:Spring封装http请求进行远程调用。
3、客户端与服务端实现rabbitmq消息通信。
客户端配置
使用 MessageChannel 封装请求
public interface SimpleMessageService {
@Output("test2020") // Channel name
MessageChannel test(); // destination = test2020
}
编写客户端api:
@RestController
public class MessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private SimpleMessageService simpleMessageService;
@GetMapping("/stream/send")
public boolean streamSend(@RequestParam String message) {
// 获取 MessageChannel
MessageChannel messageChannel = simpleMessageService.test();
Map<String, Object> headers = new HashMap<>();
headers.put("charset-encoding", "UTF-8");
headers.put("content-type", MediaType.TEXT_PLAIN_VALUE);
return messageChannel.send(new GenericMessage(message, headers));
}
}
紧接着,启动项激活并引入 SimpleMessageService:
@EnableBinding(SimpleMessageService.class) // 激活并引入 SimpleMessageService
服务端配置
类似地,使用 SubscribableChannel 封装接收类:
public interface SimpleMessageReceiver {
@Input("test2020")
SubscribableChannel test();
}
紧接着,启动类激活并引入 SimpleMessageReceiver:
@EnableBinding(SimpleMessageReceiver.class) // 激活并引入 SimpleMessageReceiver
然后,务必在消息处理中做幂等性处理:
@Autowired
private SimpleMessageReceiver simpleMessageReceiver;
@PostConstruct
public void init() { // 接口编程
// 获取 SubscribableChannel
SubscribableChannel subscribableChannel = simpleMessageReceiver.gupao();
subscribableChannel.subscribe(message -> {
MessageHeaders headers = message.getHeaders();
String encoding = (String) headers.get("charset-encoding");
String text = (String) headers.get("content-type");
byte[] content = (byte[]) message.getPayload();
try {
System.out.println("接受到消息:" + new String(content, encoding));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
});
}
@StreamListener("test2020") // Spring Cloud Stream 注解驱动
public void onMessage(byte[] data) {
System.out.println("onMessage(byte[]): " + new String(data));
}
@StreamListener("test2020") // Spring Cloud Stream 注解驱动
public void onMessage(String data) {
System.out.println("onMessage(String) : " + data);
}
@StreamListener("test2020") // Spring Cloud Stream 注解驱动
public void onMessage2(String data2) {
System.out.println("onMessage2(String) : " + data2);
}
@ServiceActivator(inputChannel = "test2020") // Spring Integration 注解驱动
public void onServiceActivator(String data) {
System.out.println("onServiceActivator(String) : " + data);
}
注意:相同的编程模型重复执行,例如
@StreamListener
,不同的编程模型轮流执行
5、启动并测试
分别启动, spring-cloud-client-application项目、spring-cloud-server-application项目、zookeeper服务与rabbitmq服务,rabbitmq服务搭建可以参考本人往期rabbitmq博客。
现在我们访问:
http://localhost:8888/stream/send?message=test
访问RabbitMQ管理页面:
后记
本节代码地址:Spring Cloud Steam
更多架构知识,欢迎关注本套Java系列文章:Java架构师成长之路