https://rabbitmq.com/ttl.html
https://rabbitmq.com/dlx.html
https://rabbitmq.com/tutorials/tutorial-six-java.html
https://gitee.com/zhuqin2016/java-simple-demo/tree/master/rabbitmq-demo/src/main/java/org/zhuqin/simpledemo/rabbitmq
本文讲解 RabbitMQ 的两个进阶用法:
- 延时队列
- RPC
延时队列
延时队列实现依赖 RabbitMQ 的 TTL (time to live) 和 死信(Dead Letter)机制。
- RabbitMQ 可以指定 Messages 和 Queues 的生存时间
- Dead Letter,顾名思义,死亡的消息。死亡的消息会去到死信交易所-Dead Letter Exchange(DLX)。队列可以通过
dead-letter-exchange
参数指定其产生的死信的 DLX。哪些消息会被认为是死信呢?- 被消费端拒绝接收
basic.reject
的消息 - 被消费端不签收
basic.nack
且不重新入队的消息 - TTL 过期的消息
- 因为队列长度限制导致丢弃的消息
- 被消费端拒绝接收
了解了 TTL 和 Dead Letter 之后,我们可以按照下图的思路实现延时队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3MGYwJv-1680188953595)(null#lake_card_v2=eyJ0eXBlIjoiZ3JhcGh2aXoiLCJjb2RlIjoiZGlncmFwaCBmaW5pdGVfc3RhdGVfbWFjaGluZSB7XG5cdHJhbmtkaXI9TFI7XG5cdHNpemU9XCI3LDNcIlxuICBub2RlIFtzaGFwZSA9IGNpcmNsZV07UHVibGlzaGVyO1xuICBQdWJsaXNoZXIgLT4gUUEgWyBsYWJlbCA9IFwiVFRMX01TR1wiXTtcbiAgUUEgLT4gRExYIFsgbGFiZWwgPSBcIkRlYWRfTGV0dGVyXCJdO1xuICBETFggLT4gUUIgWyBsYWJlbCA9IFwiTVNHX3dpdGhfUm91dGluZ0tleVwiXTtcbiAgUUIgLT4gQ29uc3VtZXIgWyBsYWJlbCA9IFwiTVNHXCJdO1xufSIsInVybCI6Imh0dHBzOi8vY2RuLm5sYXJrLmNvbS95dXF1ZS9fX2dyYXBodml6LzAzZWRjZWQwNjBkZmVjNjk5YWQ1Y2YzYzdhMzVjN2JkLnN2ZyIsImlkIjoicnF0Q1QiLCJtYXJnaW4iOnsidG9wIjp0cnVlLCJib3R0b20iOnRydWV9LCJjYXJkIjoiZGlhZ3JhbSJ9)]消息发布者先发送设置了 TTL 的消息给队列 QA,在消息过期后变成死信队列,自动转发到死信交易所 DLX,DLX 再把消息推送给与其绑定的队列 QB,QB再将消息分发给消费者。
发布者实现
- 声明死信交易所 DLX
- 声明队列 QB
- 绑定 DLX 和 QB,并指定 RoutingKey
- 生命队列 QA,指定 QA 的 DLX,指定 QA 的死信路由到 DLX 时使用的 RoutingKey
- 发送延时 10s 的消息
- 发送延时 1s 的消息
消费者实现
- 声明队列 QB
- 监听队列 QB 的消息
执行结果
- 启动发布者
- 启动消费者
消费者控制台:
可以看到延时 1s 的消息在延时 10s 的消息后收到,结果明显不符合延时队列的预期,这是因为 RabbitMQ 的队列默认是先进先出的(除了优先队列),10s 的消息到期出队后,1s 的消息再出队。
如何避免此问题:
- 保证后发布的消息比先发布的消息存活的久
- 声明多个接收 TTL 消息的队列,每个队列只接受 TTL 时间固定的消息
RPC(Remote Produce Call 远程调用)
消息队列也能实现 RPC 调用!
调用方 Client 发布消息(请求)到队列 rpc_queue
,被调用方 Server 从rpc_queue
接收消息(请求),Server 将请求响应结果发布到请求指定的队列 reply_to
,Client 从 reply_to
获取到请求结果
调用方实现
- 声明全局 Map 存放请求标识和响应实例(CompletableFuture)的映射
CompletableFuture.get
时会阻塞,等待其他线程调用CompletableFuture.complete
- 声明回调队列,即接收响应结果的队列
- 异步监听回调队列的消息,收到消息根据消息携带的标识
correlationId
找到对应的响应实例,调用CompletableFuture.complete
完成响应 - 生成消息携带的标识
correlationId
- 将响应实例放到全局 Map
- 将请求消息发布到被调用方声明的队列
- 调用
CompletableFuture.get
等待完成响应
被调用方实现
- 声明接收请求消息的队列
- 监听请求消息
- 接受到请求,将响应结果发布到请求消息指定的回调队列