一、介绍
什么是MQ
MQ(Message Queue)消息队列,通过典型的 生产者 和 消费者 模型,生产者向消息队列中生产信息,消费者不断地从队列中获取信息,它们是异步的。
主流的MQ有RabbitMQ、ActiveMQ、kafka、阿里巴巴开发的RocketMQ等
默认端口号:15672
二、工作模式
**第一种模型(直连)——直连模式
**
P:生产者,也就是要发送消息的程序
C:消费者,消息的接受者,会一直等待消息到来
queue:消息队列,图中红色部分,可以缓存消息,生产者向其投递消息,消费者从其取出消息
第二种模型(work queue)(平均分配)——work模式
P:生产者,任务的发布者
C1:消费者1,领取任务并完成任务,假设完成速度较慢
C2:消费者2,领取任务并完成任务,假设完成速度较快
也可以
生产者将消息放入队列
消费者1,消费者2同时监听同一个队列,C1 C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息
隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize) 保证一条消息只能被一个消费者使用
第三种模型(fanout 广播)——订阅模式
可以有多个消费者
每个消费者有自己的 queue(队列)
每个队列要绑定 exchange(交换机)
生产者发送消息到交换机,由交换机将消息发送给绑定过的所有队列(由交换机决定)
第四种模型(Routing)——路由模式
P:生产者,向交换机发送消息,指定一个 RoutingKey
X:交换机,接收生产者消息,然后把消息传递给相应 RoutingKey 的队列
C1:消费者1,其所在队列指定了需要 RoutingKey 为 error 的消息
C2:消费者2,其所在队列制定了需要RoutingKey 为 info, error, warning 的消息
(生产者在生产好消息的时候,拿RoutingKey来锁定之后分配给哪个消费者 )
第五种——通配符模式
将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词
第六种模式——RPC模式
首先客户端通过RPC向服务端发出请求
我这里有一堆东西需要你给我处理一下,correlation_id:这是我的请求标识,erply_to:你处理完过后把结果返回到这个队列中。
服务端拿到了请求,开始处理并返回
correlation_id:这是你的请求标识 ,原封不动的给你。 这时候客户端用自己的correlation_id与服务端返回的id进行对比。是我的,就接收。
三、在SpringBoot中使用
3.1 准备与配置
第一步:在pom.xml文件中添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
第二步:配置application.yml
spring:
rabbitmq:
host: 192.168.115.100
port: 5672
username: prod
password: 123456
virtual-host: /prod
3.2 第一种模式——直连
生产者
@SpringBootTest(classes = RabbitmqSpringbootApplication.class)
@RunWith(SpringRunner.class)
public class TestRabbitMQ {
// 注入rabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
// HelloWorld模型测试
@Test
public void test(){
rabbitTemplate.convertAndSend("hello","hello world"); // 队列名 消息
}
}
消费者
@Component // 默认持久化 非独占 不自动删除队列
@RabbitListener(queuesToDeclare = @Queue(value = "hello",durable = "false",autoDelete = "true"))
public class HelloConsumer {
@RabbitHandler
public void receive(String message){
System.out.println("message:"+message);
}
}
第二种模式——work模式
生产者
// work模型测试
@Test
public void testWork(){
for(int i=1; i<=10; i++){
rabbitTemplate.convertAndSend("work","work模型"+i);
}
}
消费者
@Component
public class WorkConsumer {
// 第一个消费者
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receive1(String message){
System.out.println("message1:"+message);
}
// 第二个消费者
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receive2(String message){
System.out.println("message2:"+message);
}
}
第三种模式——fanout广播(订阅模式)
生产者
// fanout 广播模型测试
@Test
public void testFanout(){ // 交换机名 路由Key 消息
rabbitTemplate.convertAndSend("logs","","Fanout模型发送的消息");
}
消费者
@Component
public class FanoutConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 创建临时队列 或者指定@Queue("name")
exchange = @Exchange(value = "logs",type = "fanout")// 绑定的交换机
)
})
public void receive1(String message){
System.out.println("message1:"+ message);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 创建临时队列 或者指定@Queue("name")
exchange = @Exchange(value = "logs",type = "fanout")// 绑定的交换机
)
})
public void receive2(String message){
System.out.println("message2:"+ message);
}
}
第四种模型(Routing)——路由模式
生产者
// route 路由模式
@Test
public void testRoute(){
rabbitTemplate.convertAndSend("direct_exchange","info","发送路由key为info的信息");
}
消费者
@Component
public class RouteConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 创建临时队列
exchange = @Exchange(value = "direct_exchange", type = "direct"), // 指定交换机名称与类型
key = {"info","error","warning"} // 路由key
)
})
public void receive1(String message){
System.out.println("message1:"+message);
}
}
第五种模型(Routing)——通配符模式
生产者
// Topic 动态路由 订阅模式
@Test
public void testTopic(){
rabbitTemplate.convertAndSend("topic_exchange","banana.book","banana.book路由信息");
}
消费者
@Component
public class TopicConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(type = "topic",name = "topic_exchange"),
key = {"banana.*"}
)
})
public void receive1(String message){
System.out.println(message);
}
}
四、应用场景
4.1 异步处理
例如:用户注册后,利用消息队列分发任务(无需等待返回结果),同时发送邮件和短信验证(并行方式),提高处理的效率
4.2 应用解耦
例如:用户网购下单,订单系统通知库存系统,传统方式为订单系统直接调用库存系统接口。
但是如果库存系统出现故障,下单会失败。
为了这两个系统之间解耦,可以引入消息队列,订单系统向队列写入消息,库存系统向队列订阅消息。
这样一来,就算系统故障,也能保证消息不会丢失。
4.3 流量削峰
例如:秒杀商品活动,容易因为流量过大导致应用宕机。
可以在秒杀业务系统前加入消息队列,超过消息队列长度最大值的用户请求直接跳转到错误界面。
最后,系统根据消息队列中的请求信息再做处理。
来源: https://www.515code.com/posts/b6u1c1x0/