RabbitMQ简单使用
RabbitMQ简单使用
生产端
导入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
配置文件:
spring: rabbitmq: host: 192.168.31.65 port: 5672 username: guest password: guest virtualHost: /rabbitmq # 消息确认回调 交换机发的 publisher-confirm-type: correlated # 消息确认回调 queue发的 publisher-returns: true
/** * rabbit 的配置 包含交换机、channel以及两者的绑定 */ //@Configuration public class RabbitConfig { public static final String QUEUE_INFORM_EMAIL = "queue_inform_email"; public static final String QUEUE_INFORM_SMS = "queue_inform_sms"; public static final String EXCHANGE_TOPICS_INFORM="exchange_topics_inform"; public static final String ROUTINGKEY_EMAIL="email.#"; public static final String ROUTINGKEY_SMS="sms.#"; //声明交换机 @Bean(EXCHANGE_TOPICS_INFORM) public Exchange EXCHANGE_TOPICS_INFORM(){ //durable(true) 持久化,mq重启之后交换机还在 return ExchangeBuilder.topicExchange(EXCHANGE_TOPICS_INFORM).durable(true).build(); } //声明QUEUE_INFORM_EMAIL队列 @Bean(QUEUE_INFORM_EMAIL) public Queue QUEUE_INFORM_EMAIL(){ return new Queue(QUEUE_INFORM_EMAIL); } //ROUTINGKEY_EMAIL队列绑定交换机,指定routingKey @Bean public Binding BINDING_QUEUE_INFORM_EMAIL(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue, @Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_EMAIL).noargs(); } }
消费端:
导入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
配置文件:
spring: rabbitmq: host: 192.168.31.65 port: 5672 username: guest password: guest virtualHost: /rabbitmq listener: simple: acknowledge-mode: manual #开启手动确认 direct: consumers-per-queue: 3 #限流,每次3条
@Component public class RabbitMqListener implements ChannelAwareMessageListener { @RabbitListener(queues = "queue_inform_email") public void ListenQueue(Message message){ System.out.println("1"); System.out.println(message); } }
消息可靠投递:
1、生产端
// confirmCallback 发送确认 交换机收到后发的 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean b, String s) { // System.out.println(correlationData); // System.out.println(b); // System.out.println(s); } }); // returnCallback 发送确认 channel收到后发的 rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() { @Override public void returnedMessage(ReturnedMessage returnedMessage) { System.out.println("=================="); System.out.println(returnedMessage); /** * ReturnedMessage [ * message=( * Body:'你好呀????????????溜了溜了' * MessageProperties [ * headers={}, contentType=text/plain, contentEncoding=UTF-8, * contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0] * ), * replyCode=312, * replyText=NO_ROUTE, * exchange=exchange_topics_inform, * routingKey=aaa.hhh * ] */ } }); //发送 rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_TOPICS_INFORM,"email.hhh","你好呀????????????溜了溜了");
2、消费端
@Component public class RabbitMqListener implements ChannelAwareMessageListener { /** * 消费端的确认 */ @Override @RabbitListener(queues = "queue_inform_email") public void onMessage(Message message, Channel channel) throws Exception { long deliveryTag = message.getMessageProperties().getDeliveryTag(); try { System.out.println(message); // int a = 3/0; // 如果没出问题,就ACK channel.basicAck(deliveryTag,true); } catch (Exception e) { // 出了问题,NACK channel.basicNack(deliveryTag,true,true); } } }
消费端限流
消费端xml:
spring: rabbitmq: host: 192.168.31.65 port: 5672 username: guest password: guest virtualHost: /rabbitmq listener: simple: acknowledge-mode: manual #开启手动确认 direct: consumers-per-queue: 3 #限流,每次3条
RabbitMqListener:
@Component public class RabbitMqListener implements ChannelAwareMessageListener { @Override @RabbitListener(queues = "queue_inform_email") public void onMessage(Message message, Channel channel) throws Exception { long deliveryTag = message.getMessageProperties().getDeliveryTag(); try { System.out.println(message); // 如果没出问题,就ACK 开启限流后,这边ack了,才会消耗掉mq中的消息,不然数据就重回队列 channel.basicAck(deliveryTag,true); } catch (Exception e) { // 出了问题,NACK channel.basicNack(deliveryTag,true,true); } } }
TTL
TTL全称Time To Live(存活时间/过期时间)。
当消息到达存活时间后,还没有被消费,会被自动清除。
RabbitMQ可以对单个消息设置过期时间,也可以对整个队列(Queue)设置过期时间。
1、队列TTL
1.1、在后台添加:
\image-20220821192250579.png)
添加后:
1.2、代码实现
/** * rabbit 的配置 测试ttl */ @Configuration public class RabbitConfig2 { //声明交换机 @Bean("test_queue_ttl") public Exchange EXCHANGE_TOPICS_INFORM(){ //durable(true) 持久化,mq重启之后交换机还在 //声明各种模式的交换机 // return ExchangeBuilder.fanoutExchange() // return ExchangeBuilder.directExchange() return ExchangeBuilder.topicExchange("test_queue_ttl").durable(true).build(); } //声明ttl队列 @Bean("ttlQueue") public Queue getQueue(){ return QueueBuilder.durable("ttlQueue").ttl(10000).build(); } //队列绑定交换机,指定routingKey @Bean public Binding BINDING_QUEUE_INFORM_EMAIL(@Qualifier("ttlQueue") Queue queue, @Qualifier("test_queue_ttl") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("#.ttl").noargs(); } }
2、单个消息TTL
@Test public void method(){ // 消息后处理对象,设置消息的一些参数信息 MessagePostProcessor messagePostProcessor = new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { // 1、设置消息的信息 message.getMessageProperties().setExpiration("5000"); //设置消息过期时间 // 2、返回该消息 return message; } }; // 将上面的处理对象传入,就可以实现单个消息ttl rabbitTemplate.convertAndSend("test_queue_ttl","aa.ttl","hhhhhhhhhh",messagePostProcessor); }
小总结
如果设置了消息的过期时间,也设置了队列的过期时间,它以时间短的为准。
队列过期后,会将队列所有消息全部移除。
消息过期后,只有消息在队列顶端,才会判断其是否过期(移除掉)
死信队列
死信队列,英文缩写:DLX。Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。
消息成为死信的三种情况:
- 队列消息长度到达限制;
- 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
- 原队列存在消息过期设置,消息到达超时时间未被消费;
/** * rabbit 的配置 死信队列 */ @Configuration public class RabbitConfigDLX { /** * 死信队列: * 1.声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx) * 2.声明死信队列(queue_dLx)和死信交换机(exchange_dlx) * 3.正常队列绑定死信交换机 * 设置两个参数: * x-dead-letter-exchange :死信交换机名称 * x-dead-letter-routing-key:发送给死信交换机的routingkey * */ //声明正常交换机 @Bean("test_exchange_dlx") public Exchange EXCHANGE_formal(){ return ExchangeBuilder.topicExchange("test_exchange_dlx").durable(true).build(); } //声明正常队列 @Bean("test_queue_dlx") public Queue getQueueFormal(){ return QueueBuilder.durable("test_queue_dlx") // 设置过期时间和最大长度 .ttl(10000).maxLength(10) // 正常队列绑定死信交换机 .deadLetterExchange("exchange_dlx").deadLetterRoutingKey("dlx.aaa").build(); } //正常队列绑定正常交换机 @Bean public Binding BINDING_QUEUE_test(@Qualifier("test_queue_dlx") Queue queue, @Qualifier("test_exchange_dlx") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("test.dlx.*").noargs(); } //声明死信交换机 @Bean("exchange_dlx") public Exchange EXCHANGE_DLX(){ return ExchangeBuilder.topicExchange("exchange_dlx").durable(true).build(); } //声明死信队列 @Bean("queue_dlx") public Queue getQueue_DLX(){ return QueueBuilder.durable("queue_dlx").ttl(15000).maxLength(200).build(); } //死信队列绑定死信交换机 @Bean public Binding BINDING_QUEUE_dlx(@Qualifier("queue_dlx") Queue queue, @Qualifier("exchange_dlx") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("dlx.#").noargs(); } }
测试:
/** * 发送测试死信消息: * 1.过期时间 * 2.长度限制 * 3.消息拒牧 */ @Test public void testDlx(){ // 过期时间 // rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.aa","谁的父亲死了"); //长度限制 for (int i = 0; i < 20; i++) { rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.aa","谁的父亲死了"); } //消息拒牧 rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.aa","谁的父亲死了"); }
消息拒收测试的消费端配置:
/** * 测试拒收后不返回队列 */ @Override @RabbitListener(queues = "test_queue_dlx") public void onMessage(Message message, Channel channel) throws Exception { long deliveryTag = message.getMessageProperties().getDeliveryTag(); try { int a = 3/0; channel.basicAck(deliveryTag,true); } catch (Exception e) { // 出了问题,NACK channel.basicNack(deliveryTag,true,false); } }
死信队列小结:
- 死信交换机和死信队列和普通的没有区别
- 当消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
- 消息成为死信的三种情况:
- 队列消息长度到达限制;
- 消费者拒接消费消息,并且不重回队列;
- 原队列存在消息过期设置,消息到达超时时间未被消费;
延迟队列
TTL + DLX
/** * rabbit 的配置 延迟队列 */ @Configuration public class RabbitConfigOrder { /** * 延迟队列: * 1.定义正常交换机(order_exchange)和队列(order_queue) * 2.定义死信交换机(order_exchange_dlx〉和队列(order_queue_dLx)3.绑定,设置正常队列过期时间为36分钟 */ //声明正常交换机 @Bean("order_exchange") public Exchange EXCHANGE_formal(){ return ExchangeBuilder.topicExchange("order_exchange").durable(true).build(); } //声明正常队列 @Bean("order_queue") public Queue getQueueFormal(){ return QueueBuilder.durable("order_queue") // 设置过期时间和最大长度 .ttl(10000).maxLength(100) // 正常队列绑定死信交换机 .deadLetterExchange("order_exchange_dlx").deadLetterRoutingKey("dlx.aaa").build(); } //正常队列绑定正常交换机 @Bean public Binding BINDING_QUEUE_test(@Qualifier("order_queue") Queue queue, @Qualifier("order_exchange") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("order.dlx.*").noargs(); } //声明死信交换机 @Bean("order_exchange_dlx") public Exchange EXCHANGE_DLX(){ return ExchangeBuilder.topicExchange("order_exchange_dlx").durable(true).build(); } //声明死信队列 @Bean("order_queue_dlx") public Queue getQueue_DLX(){ return QueueBuilder.durable("order_queue_dlx").ttl(20000).maxLength(200).build(); } //死信队列绑定死信交换机 @Bean public Binding BINDING_QUEUE_dlx(@Qualifier("order_queue_dlx") Queue queue, @Qualifier("order_exchange_dlx") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("dlx.#").noargs(); } }
测试:
/** * 发送测试延迟队列 */ @Test public void testOrder(){ // 消息后处理对象,设置消息的一些参数信息 MessagePostProcessor messagePostProcessor = new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { // 1、设置消息的信息 message.getMessageProperties().setExpiration("15000"); //设置消息过期时间 // 2、返回该消息 return message; } }; for (int i = 0; i < 20; i++) { rabbitTemplate.convertAndSend("order_exchange","order.dlx.aa","谁的父亲死了",messagePostProcessor); } }
小结:
-
延迟队列指消息进入队列后,可以被延迟一定时间,再进行消费。
-
RabbitMQ没有提供延迟队列功能,但是可以使用:TTL + DLX来实现延迟队列效果。
消息追踪
在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况。对于RabbitMQ而言,可能是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;也有可能是因为交换器与队列之间不同的转发策略;甚至是交换器并没有与任何队列进行绑定,生产者又不感知或者没有采取相应的措施;另外RabbitMQ本身的集群策略也可能导致消息的丢失。这个时候就需要有一个较好的机制跟踪记录消息的投递过程,以此协助开发和运维人员进行问题的定位。
在RabbitMQ中可以使用Firehose和rabbitmq_tracing插件功能来实现消息追踪。
Firehose
firehose的机制是将生产者投递给rabbitmq的消息,rabbitmq投递给消费者的消息按照指定的格式发送到默认的exchange上。这个默认的exchange的名称为amq.rabbitmq.trace,它是一个topic型的exchange。发送到这个exchange上的消息的routing key为publish.exchangename和deliver.queuename。其中exchangename和queuename为实际exchange和queue的名称,分别对应生产者投递到exchange的消息,和消费者从queue上获取的消息。
注意:打开trace会影响消息写入功能,适当打开后请关闭。
rabbitmqctl trace_on:开启Firehose命令
rabbitmqctl trace_off:关闭Firehose命令
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具