图片来自百战尚学堂
目录
RabbitMQ的优点
SpringBoot整合RabbitMQ
简单模式
工作队列模式
发布订阅模式
路由模式
通配符模式
消息的可靠性传递
确认模式
返回模式
Ack模式
1、削峰填谷
在电商平台中,尝尝会有一些大型的活动,例如:双十一,618等等,这种活动会使服务器的工作量瞬间提升,但是平时点击量却是呈现出一种平原的样子,只有在有活动的时候突然点击量飙升,那么服务器有可能会因为处理不过来而崩溃 ,所以RabbitMQ的优点之一就是削峰填谷
2、应用解耦
一个电商平台项目会用很多各种系统,例如用户下单会调用订单系统,订单系统会调用库存系统、支付系统、物流系统完成业务,此时会产生两个问题:
1、如果其中某一个系统发生了故障,就会导致整个平台崩溃
2、如果我们需要添加一个系统,此时就必须更改源代码
如果在系统中引入MQ,即订单系统将消息先发送到MQ中,MQ再转发到其他系统,则会解决以下问题:
3、异步提速
如果订单系统同步访问每个系统,则用户下单等待时长如下:
如果引入MQ,则用户下单等待时长如下:
RabbitMQ有很多种交换机,不同的交换机有不同的模式:简单模式,工作队列模式,发布订阅模式,路由模式,通配符模式,下面我们就来讲讲最常用的通配符模式。
通配符模式(Topic)是在路由模式的基础上,给队列绑定带通配符的路由关键字,只要消息的RoutingKey能实现通配符匹配,就会将消息转发到该队列。通配符模式比路由模式更灵活,使用topic交换机。
通配符规则:
1、通过配置类将交换机和队列创建出来并绑定
@Configuration
public class RabbitConfig {
private final String NORMAL_EXCHANGE="normal_exchange";//普通交换机
private final String NORMAL_QUEUE="normal_queue";//普通队列
//普通交换机
@Bean("normal_exchange")
public Exchange Exchange(){
//topicExchange代表这是一个通配符交换机
//durable代表消息持久化,rabbitmq重启消息不回丢失
return ExchangeBuilder.topicExchange(NORMAL_EXCHANGE).durable(true).build();
}
//普通队列绑定死信交换机以及死信路由关键字
@Bean("normal_queue")
public Queue Queue(){
//durable代表消息持久化
//ttl代表消息事件,1000ms=1s
//maxLength代表队列最大长度
return QueueBuilder.durable(NORMAL_QUEUE).ttl(10000).maxLength(10).build();
}
//普通交换机绑定普通队列
@Bean
public Binding bingQueue(@Qualifier("normal_exchange") Exchange exchange,@Qualifier("normal_queue") Queue queue){
//bind队列to交换机with路由关键字
return BindingBuilder.bind(queue).to(exchange).with("normal_routing").noargs();
}
}
2、在测试类中生产消息
@SpringBootTest
class BlograbbitApplicationTests {
//SpringBoot整合RabbitMQ可以使用RabbitTemplate省略创建连接和信道的过程
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void publisher(){
/**
* 参数一:交换机名
* 参数二:路由关键字
* 参数三:消息内容
*/
rabbitTemplate.convertAndSend("normal_exchange","normal_routing","你好,消费者!");
}
}
打开RabbitMQ的管控台插件查看队列中是否有消息
3、编写消费者代码
@Component
public class RabbitConsumer {
//监听队列消费信息
@RabbitListener(queues = "normal_queue")//监听的队列名
public void listen(String message){
System.out.println(message);
}
}
4、当我们运行启动类即可消费消息(注意因为我们在定义的时候定义了队列中消息的存活时间为10s,所以需要尽快消费,否则就会过期无法消费)
RabbitMQ消息投递的路径为:
生产者--->交换机--->队列--->消费者
在RabbitMQ工作的过程中,每个环节消息都可能传递失败,那么RabbitMQ是如何监听消息是否成功投递的呢?
通过更改之前已经写好的类实现消息的可靠性传递
@SpringBootTest
class BlograbbitApplicationTests {
//SpringBoot整合RabbitMQ可以使用RabbitTemplate省略创建连接和信道的过程
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void publisher(){
//确认模式回调方法
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
if (b){
System.out.println("confirm接收成功");
}else{
System.out.println("confirm接受失败");
}
}
});
//返回模式只有在交换机失败发送消息给队列时才会调用
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
System.out.println("消息对象:"+returned.getMessage());
System.out.println("错误码:"+returned.getReplyCode());
System.out.println("错误信息:"+returned.getReplyText());
System.out.println("交换机:"+returned.getExchange());
System.out.println("路由键:"+returned.getRoutingKey());
// 处理消息...
}
});
/**
* 参数一:交换机名
* 参数二:路由关键字
* 参数三:消息内容
*/
rabbitTemplate.convertAndSend("normal_exchange","normal_routing","你好,消费者!");
}
}
在RabbitMQ中,消费者接收到消息后会向队列发送确认签收的消息,只有确认签收的消息才会被移除队列。这种机制称为消费者消息确认(Consumer Acknowledge,简称Ack)。类似快递员派送快递也需要我们签收,否则一直存在于快递公司的系统中。
消息分为自动确认和手动确认。自动确认指消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除。但是在实际开发中,收到消息后可能业务处理出现异常,那么消息就会丢失。此时需要设置手动签收,即在业务处理成功再通知签收消息,如果出现异常,则拒签消息,让消息依然保留在队列当中。(默认为手动签收)
@Component
public class RabbitConsumer {
//监听队列消费信息
@RabbitListener(queues = "normal_queue")//监听的队列名
public void listen(Message message, Channel channel) throws IOException, InterruptedException {
//获取消息投递序号,消息每次投递序号+1
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try{
//模拟发生错误
int i = 1/0;
//签收消息
/**
* 参数一:消息投递序号
* 参数二:是否一次签收多条消息
*/
channel.basicAck(deliveryTag,true);
}catch (Exception ex){
System.out.println("消息消费失败!");
Thread.sleep(2000);
// 拒签消息
/**
* 参数1:消息投递序号
* 参数2:是否一次可以拒签多条消息
* 参数3:拒签后消息是否重回队列
*/
channel.basicNack(deliveryTag,true,true);
}
System.out.println(message);
}
}