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。

消息成为死信的三种情况:

  1. 队列消息长度到达限制;
  2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
  3. 原队列存在消息过期设置,消息到达超时时间未被消费;
/**
* 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);
}
}

死信队列小结:

  1. 死信交换机和死信队列和普通的没有区别
  2. 当消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
  3. 消息成为死信的三种情况:
    1. 队列消息长度到达限制;
    2. 消费者拒接消费消息,并且不重回队列;
    3. 原队列存在消息过期设置,消息到达超时时间未被消费;

延迟队列

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);
}
}

小结:

  1. 延迟队列指消息进入队列后,可以被延迟一定时间,再进行消费。

  2. 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命令

rabbitmq_tracing

RabbitMQ应用问题

集群

posted @   z-laoyao  阅读(188)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示