一、MQ
MQ,中文是消息队列(MessageQueue),字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。
比较常见的MQ实现:
- ActiveMQ
- RabbitMQ
- RocketMQ
- Kafka
二、RabbitMQ
异步通讯带来的优缺点
好处:
- 吞吐量提升:无需等待订阅者处理完成,响应更快速
- 故障隔离:服务没有直接调用,不存在级联失败问题
- 调用间没有阻塞,不会造成无效的资源占用
- 耦合度极低,每个服务都可以灵活插拔,可替换
- 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件
缺点:
- 架构复杂了,业务没有明显的流程线,不好管理
- 需要依赖于Broker的可靠、安全、性能
三、前置工作
导入依赖
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置信息
spring:
rabbitmq:
host: # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: # 用户名
password: # 密码
四、五种模式及代码实现
1.Basic Queue 简单队列模型
代码实现:
发送消息:
rabbitTemplate.convertAndSend("mySimple.queue",message.getBytes(StandardCharsets.UTF_8));
监听消费消息:
@RabbitListener(queues = "mySimple.queue")
public void handleSimple(String message) throws InterruptedException {
Thread.sleep(300);
log.info("消费者收到消息:{}",message);
}
2.WorkQueue 工作队列
发送消息
/**
* workQueueTest模式测试
*/
@Test
void workQueueTest() {
for (int i = 1; i <= 50; i++) {
String message = "Hello,WorkQueue!!!"+i;
rabbitTemplate.convertAndSend("mySimple.queue",message.getBytes(StandardCharsets.UTF_8));
}
System.out.println("消息发送成功");
}
消费消息:
@RabbitListener(queues = "mySimple.queue")
public void handleSimple(String message) throws InterruptedException {
Thread.sleep(300);
log.info("消费者收到消息:{}",message);
}
可以配置以下信息,实现能者多劳:
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息
3.Fanout 发布订阅
代码实现:
发送消息:
@Test
void fanoutTest() {
String message = "Hello,Fanout!!!";
rabbitTemplate.convertAndSend("itcast.fanout","",message);
System.out.println("消息发送成功");
}
监听消费消息:
//===================fanout模式
@RabbitListener(queues = "fanout.queue1")
public void handleQueueFirst(String message) throws InterruptedException {
log.info("fanout.queue1消息:{}",message);
}
@RabbitListener(queues = "fanout.queue2")
public void handleQueueSecond(String message) throws InterruptedException {
log.info("fanout.queue2消息:{}",message);
}
以上需要配置文件声明交换机并绑定队列到交换机:
/**
* 声明交换机名称
* @return
*/
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("itcast.fanout");
}
/**
* 声明队列一名称
* @return
*/
@Bean
public Queue queueFirst(){
return new Queue("fanout.queue1");
}
/**
* 声明队列二名称
* @return
*/
@Bean
public Queue queueSecond(){
return new Queue("fanout.queue2");
}
/**
* 绑定队列一到交换机
* @param queueFirst
* @param fanoutExchange
* @return
*/
@Bean
public Binding bindingQueueFirst(Queue queueFirst,FanoutExchange fanoutExchange){
return BindingBuilder.bind(queueFirst).to(fanoutExchange);
}
/**
* 绑定队列二到交换机
* @param queueSecond
* @param fanoutExchange
* @return
*/
@Bean
public Binding bindingQueueSecond(Queue queueSecond,FanoutExchange fanoutExchange){
return BindingBuilder.bind(queueSecond).to(fanoutExchange);
}
4. Direct 路由模式
代码实现:
发送消息:
/**
* direct模式测试
*/
@Test
void directTest() {
rabbitTemplate.convertAndSend("itheima.direct","spring","direct spring message");
rabbitTemplate.convertAndSend("itheima.direct","summer","direct summer message");
rabbitTemplate.convertAndSend("itheima.direct","winter","direct winter message");
rabbitTemplate.convertAndSend("itheima.direct","autumn","direct autumn message");
System.out.println("消息发送成功");
}
监听消费消息:
//===================direct模式
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "itheima.direct.queue1"),
exchange = @Exchange(name = "itheima.direct",type = ExchangeTypes.DIRECT),
key={"spring","summer"}
))
public void handleDircetQueueFirst(String message)
{
log.info("direct.queue1:{}",message);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "itheima.direct.queue2"),
exchange = @Exchange(name = "itheima.direct",type = ExchangeTypes.DIRECT),
key={"summer","winter"}
))
public void handleDircetQueueSecond(String message){
log.info("direct.queue2:{}",message);
}
5. Topic 主题模式
代码实现:
发送消息:
/**
* topic模式测试
*/
@Test
void topicTest() {
rabbitTemplate.convertAndSend("itcast.topic","china.Number.One","chinaNumberOne");
rabbitTemplate.convertAndSend("itcast.topic","china.NumberOne.news","chinaNumberOnenews");
rabbitTemplate.convertAndSend("itcast.topic","NumberOne","NumberOne");
rabbitTemplate.convertAndSend("itcast.topic","my.news","mynews");
System.out.println("消息发送成功");
}
监听消费消息:
//===================topic模式
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"),
exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
key="china.#"
))
public void handleTopicQueueFirst(String message){
log.info("topic.queue1:{}",message);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue2"),
exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
key="#.news"
))
public void handleTopicQueueSecond(String message){
log.info("topic.queue2:{}",message);
}
6. 消息转换器
所需依赖:
可以使用JSON方式来做序列化和反序列化。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.5</version>
</dependency>
需要配置消息转换器:
/**
* 消息转换器配置类
* @Description
* @Author Wcmy.m
* @Date 2023-05-17 17:46:42
*/
@Configuration
public class MessageConverterConfig {
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
}
实例:
发送消息:
/**
* 消息转换器测试
*/
@Test
void simpleTest() {
User user = User.builder()
.id(11L)
.name("张三")
.gender(1).build();
rabbitTemplate.convertAndSend("mySimple.queue",user);
System.out.println("消息发送成功");
}
消费者监听消费消息:
/**
* 消息转换器
*/
@RabbitListener(queues = "mySimple.queue")
public void handleSimpleObject(User user) throws InterruptedException {
//Thread.sleep(300);
log.info("消费者收到实体:{}",user);
}