fila de letras mortas
Visão geral
Se não houver consumidor para processar os dados na fila de mensagens, ele sempre ocupará o espaço da fila de mensagens.
Por exemplo, no cenário de retirada de passagem, depois que um usuário fizer um pedido de uma passagem de trem de alta velocidade, ele pegará um assento e efetuará o pagamento. No entanto, se o usuário não pagar a tempo após fazer o pedido , o ingresso não pode ser ocupado por este usuário o tempo todo, pois será ocupado por outros. Você não pode mais comprar este ingresso, então ele expirará após um período de tempo, permitindo que este ingresso continue a ser adquirido por outros .
Neste momento, você pode usar a fila de devoluções para processar ainda mais os pedidos que o usuário não pagou ao longo do tempo ou que o usuário cancelou ativamente.
Então, como construir esse modelo de uso? Na verdade, é essencialmente uma troca de letras mortas + fila de letras mortas .
Quando uma mensagem na fila normal é determinada como uma mensagem morta, ela será enviada para o switch de mensagens mortas correspondente e, em seguida, enviada para a fila de mensagens mortas através do switch.A fila de mensagens mortas também tem um consumidor correspondente para processar o mensagem.
Geralmente há três situações em que uma letra morta é considerada morta:
- A mensagem foi rejeitada (
basic.reject
/basic.nack
) erequeue = false
. - A mensagem expirou e não foi consumida.
- A fila de mensagens atingiu seu tamanho máximo.
gerar letras mortas
Mensagem rejeitada
-
Crie um novo switch de mensagens não entregues e uma fila de mensagens não entregues na classe de configuração e vincule-os.
import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Exchange; import org.springframework.amqp.core.ExchangeBuilder; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * RabbitMQ配置类 */ @Configuration public class RabbitMqConfig { ... /** * 定义消息队列 * @return 消息队列对象 */ @Bean("testQueue") public Queue queue(){ return QueueBuilder // 非持久化类型 .nonDurable("test_springboot") // 指定死信交换机 .deadLetterExchange("dl.direct") // 指定死信RoutingKey .deadLetterRoutingKey("dl_test_springboot_key") .build(); } /** * 构建死信交换机 * @return 死信交换机 */ @Bean public Exchange dlExchange(){ // 创建一个新的死信交换机 return ExchangeBuilder.directExchange("dl.direct").build(); } /** * 构建死信队列 * @return 死信队列 */ @Bean public Queue dlQueue(){ return QueueBuilder .nonDurable("dl_test_springboot") .build(); } /** * 死信交换机和死信队列绑定 * @param exchange 死信交换机 * @param queue 死信队列 * @return 绑定对象 */ @Bean public Binding dlBinding(@Qualifier("dlExchange") Exchange exchange, @Qualifier("dlQueue") Queue queue){ return BindingBuilder .bind(queue) .to(exchange) .with("dl_test_springboot_key") .noargs(); } ... }
-
Ouça as mensagens da fila normal e da fila de mensagens não entregues.
import cn.codesail.rabbitmq.entity.User; import com.rabbitmq.client.Channel; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; /** * 直连队列监听器 */ @Component public class DirectListener { /** * 监听正常队列消息 */ @RabbitListener(queues = "test_springboot", messageConverter = "jackson2JsonMessageConverter") public void receiver(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, User user) throws Exception { // 拒绝消息。第二个参数为true则消息返回队列,第二个参数为false则消息不返回队列,成为死信 channel.basicReject(deliveryTag, false); System.out.println("正常队列接收到消息:" + user); } /** * 监听死信队列消息 */ @RabbitListener(queues = "dl_test_springboot", messageConverter = "jackson2JsonMessageConverter") public void receiverDl(User user) { System.out.println("死信队列接收到消息:" + user); } }
Se o ouvinte de mensagens da fila normal rejeitar a mensagem e não devolvê-la à fila, ela se tornará uma mensagem morta e será recebida pelo ouvinte da fila de mensagens mortas.
-
Exclua a fila original. A fila vinculada à fila de devoluções só pode ser criada após a fila original ser excluída.
-
Implementar produtores.
import cn.codesail.rabbitmq.entity.User; import org.junit.jupiter.api.Test; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class RabbitMqSpringBootTests { /** * RabbitTemplate封装了大量的RabbitMQ操作,已经由Starter提供,因此直接注入使用即可 */ @Autowired private RabbitTemplate rabbitTemplate; /** * 生产者 */ @Test void producer() { // 发送Json消息 User user = new User(); user.setName("张三"); user.setAge(18); rabbitTemplate.convertAndSend("amq.direct", "test_springboot_key", user); } }
-
Inicie o produtor para enviar mensagens:
Como você pode ver, a fila de mensagens não entregues recebeu a mensagem.
A mensagem expirou e não foi consumida
-
Defina o valor TTL da fila.
import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * RabbitMQ配置类 * * @author CodeSail */ @Configuration public class RabbitMqConfig { ... /** * 定义消息队列 * @return 消息队列对象 */ @Bean("testQueue") public Queue queue(){ return QueueBuilder // 非持久化类型 .nonDurable("test_springboot") // 指定死信交换机 .deadLetterExchange("dl.direct") // 指定死信RoutingKey .deadLetterRoutingKey("dl_test_springboot_key") // 如果5秒没处理,就自动删除 .ttl(5000) .build(); } ... }
-
Cancele a escuta normal da fila.
import cn.codesail.rabbitmq.entity.User; import com.rabbitmq.client.Channel; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; /** * 直连队列监听器 */ @Component public class DirectListener { /** * 监听正常队列消息 */ // @RabbitListener(queues = "test_springboot", messageConverter = "jackson2JsonMessageConverter") // public void receiver(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, User user) throws Exception { // // 拒绝消息。第二个参数为true则消息返回队列,第二个参数为false则消息不返回队列,成为死信 // channel.basicReject(deliveryTag, false); // System.out.println("正常队列接收到消息:" + user); // } /** * 监听死信队列消息 */ @RabbitListener(queues = "dl_test_springboot", messageConverter = "jackson2JsonMessageConverter") public void receiverDl(User user) { System.out.println("死信队列接收到消息:" + user); } }
-
Implementar produtores.
import cn.codesail.rabbitmq.entity.User; import org.junit.jupiter.api.Test; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.concurrent.TimeUnit; @SpringBootTest class RabbitMqSpringBootTests { /** * RabbitTemplate封装了大量的RabbitMQ操作,已经由Starter提供,因此直接注入使用即可 */ @Autowired private RabbitTemplate rabbitTemplate; /** * 生产者 */ @Test void producer() throws InterruptedException { // 发送Json消息 User user = new User(); user.setName("张三"); user.setAge(18); rabbitTemplate.convertAndSend("amq.direct", "test_springboot_key", user); } }
-
Exclua a fila original. Somente depois de excluir a fila original você poderá criar uma fila com um TTL vinculado à fila de devoluções.
-
Inicie o serviço e ouça as mensagens.
-
Inicie o produtor para enviar mensagens e aguarde 5 segundos:
Como você pode ver, a fila de mensagens não entregues recebeu a mensagem.
A fila de mensagens atinge o comprimento máximo
-
Defina o comprimento máximo da mensagem da fila.
import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Exchange; import org.springframework.amqp.core.ExchangeBuilder; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * RabbitMQ配置类 */ @Configuration public class RabbitMqConfig { ... /** * 定义消息队列 * @return 消息队列对象 */ @Bean("testQueue") public Queue queue(){ return QueueBuilder // 非持久化类型 .nonDurable("test_springboot") // 指定死信交换机 .deadLetterExchange("dl.direct") // 指定死信RoutingKey .deadLetterRoutingKey("dl_test_springboot_key") // 最大长度设定为3 .maxLength(3) .build(); } ... }
-
Cancele a escuta normal da fila.
import cn.codesail.rabbitmq.entity.User; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; /** * 直连队列监听器 */ @Component public class DirectListener { /** * 监听正常队列消息 */ // @RabbitListener(queues = "test_springboot", messageConverter = "jackson2JsonMessageConverter") // public void receiver(User user) { // System.out.println("正常队列接收到消息:" + user); // } /** * 监听死信队列消息 */ @RabbitListener(queues = "dl_test_springboot", messageConverter = "jackson2JsonMessageConverter") public void receiverDl(User user) { System.out.println("死信队列接收到消息:" + user); } }
-
Exclua a fila original. Somente após a fila original ser excluída é que uma fila com um comprimento máximo definido que está vinculada à fila de devoluções pode ser criada.
-
Defina produtor.
import cn.codesail.rabbitmq.entity.User; import org.junit.jupiter.api.Test; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.concurrent.TimeUnit; @SpringBootTest class RabbitMqSpringBootTests { /** * RabbitTemplate封装了大量的RabbitMQ操作,已经由Starter提供,因此直接注入使用即可 */ @Autowired private RabbitTemplate rabbitTemplate; /** * 生产者 */ @Test void producer() throws InterruptedException { for (int i = 0; i < 4; i++) { User user = new User(); user.setName("张三" + i); user.setAge(18); rabbitTemplate.convertAndSend("amq.direct", "test_springboot_key", user); } } }
-
Inicie o produtor para enviar mensagens.
Como você pode ver, o primeiro elemento da fila é eliminado e se torna letra morta.
A fila é semelhante a um pipeline: quando o pipeline estiver cheio, a última pessoa a entrar expulsará a primeira.
- ambiente
- JDK 17.0.6
- Maven 3.6.3
- SpringBoot3.0.4
- Spring-boot-starter-amqp 3.0.4
- jackson-databind 2.14.2
- referência