RabbitMQ 发布订阅-实现延时重试队列(参考)
RabbitMQ消息处理失败,我们会让失败消息进入重试队列等待执行,因为在重试队列距离真正执行还需要定义的时间间隔,因此,我们可以将重试队列设置成延时处理。今天参考网上其他人的实现,简单梳理下消息延时重试执行的思路。
消费失败后,自动延时将消息重新投递,当达到一定的重试次数后,将消息投递到失败消息队列,等待人工介入处理。在这里我们一步一步实现一个带有失败重试功能的发布订阅组件,使用该组件后可以非常简单的实现消息的发布订阅。
业务背景
- 结合RabbitMQ的Topic模式和Work Queue模式实现生产方产生消息,消费方按需订阅,消息投递到消费方的队列之后,多个worker同时对消息进行消费
- 结合RabbitMQ的 Message TTL 和 Dead Letter Exchange 实现消息的延时重试功能
- 消息达到最大重试次数之后,将其投递到失败队列,等待人工介入处理bug后,重新将其加入队列消费
执行流程图
- 生产者发布消息到主Exchange
- 主Exchange根据Routing Key将消息分发到对应的消息队列
- 多个消费者的worker进程同时对队列中的消息进行消费,因此它们之间采用“竞争”的方式来争取消息的消费
- 消息消费后,不管成功失败,都要返回ACK消费确认消息给队列,避免消息消费确认机制导致重复投递,同时,如果消息处理成功,则结束流程,否则进入重试阶段
- 如果重试次数小于设定的最大重试次数(默认为3次),则将消息重新投递到Retry Exchange的重试队列
- 重试队列不需要消费者直接订阅,它会等待消息的有效时间过期之后,重新将消息投递给Dead Letter Exchange,我们在这里将其设置为主Exchange,实现延时后重新投递消息,这样消费者就可以重新消费消息
- 如果三次以上都是消费失败,则认为消息无法被处理,直接将消息投递给Failed Exchange的Failed Queue,这时候应用可以触发报警机制,以通知相关责任人处理
- 等待人工介入处理(解决bug)之后,重新将消息投递到主Exchange,这样就可以重新消费了
技术实现:
创建Exchange
为了实现消息的延时重试和失败存储,我们需要创建三个Exchange来处理消息。
- master 主Exchange,发布消息时发布到该Exchange
- master.retry 重试Exchange,消息处理失败时(3次以内),将消息重新投递给该Exchange
- master.failed 失败Exchange,超过三次重试失败后,消息投递到该Exchange
所有的Exchange声明(declare)必须使用以下参数
参数 | 值 | 说明 |
---|---|---|
exchange | - | Exchange名称 |
type | topic | Exchange 类型 |
passive | false | 如果Exchange已经存在,则返回成功,不存在则创建 |
durable | true | 持久化存储Exchange,这里仅仅是Exchange本身持久化,消息和队列需要单独指定其持久化 |
no-wait | false | 该方法需要应答确认 |
在RabbitMQ的管理界面中,我们可以看到创建的三个Exchange
消息发布
消息发布时,使用basic_publish
方法,参数如下
参数 | 值 | 说明 |
---|---|---|
message | - | 发布的消息对象 |
exchange | master | 消息发布到的Exchange |
routing-key | - | 路由KEY,用于标识消息类型 |
mandatory | false | 是否强制路由,指定了该选项后,如果没有订阅该消息,则会返回路由不可达错误 |
immediate | false | 指定了当消息无法直接路由给消费者时如何处理 |
发布消息时,对于message
对象,其内容使用json编码后的字符串,同时消息进行持久化
消息订阅
消息订阅的实现相对复杂一些,需要完成队列的声明以及队列和Exchange的绑定
Declare Queue
对于每一个订阅消息的服务,都必须创建一个该服务对应的队列,将该队列绑定到关注的路由规则,这样之后,消息生产者将消息投递给Exchange之后,就会按照路由规则将消息分发到对应的队列供消费者消费了。
消费服务需要declare三个队列
[queue_name]
队列名称,格式符合[服务名称]@订阅服务标识
[queue_name]@retry
重试队列[queue_name]@failed
失败队列
Declare队列时,参数规定规则如下
参数 | 值 | 说明 |
---|---|---|
queue | - | 队列名称 |
passive | false | 队列不存在则创建,存在则直接成功 |
durable | true | 队列持久化 |
exclusive | false | 排他,指定该选项为true则队列只对当前连接有效,连接断开后自动删除 |
no-wait | false | 该方法需要应答确认 |
auto-delete | false | 当不再使用时,是否自动删除 |
对于@retry
重试队列,需要指定额外参数
'x-dead-letter-exchange' => 'master'
'x-dead-letter-routing-key' => [queue_name],
'x-message-ttl' => 30 * 1000 // 重试时间设置为30s
这里的两个header字段的含义是,在队列中延迟30s后,将该消息重新投递到x-dead-letter-exchange
对应的Exchange中,并且routing key指定为消费队列的名称,这样就可以实现消息只投递给原始出错时的队列,避免消息重新投递给所有关注当前routing key的消费者了。
在RabbitMQ的管理界面中,Queues部分可以看到我们创建的三个队列
查看队列的详细信息,我们可以看到 queueName@retry 队列与其它两个队列的不同
队列和Exchange绑定
创建完队列之后,需要将队列与Exchange绑定(bind
),不同队列需要绑定到之前创建的对应的Exchange上面
Queue | Exchange |
---|---|
[queue_name] | master |
[queue_name]@retry | master.retry |
[queue_name]@failed | master.failed |
绑定时,需要提供订阅的路由KEY,该路由KEY与消息发布时的路由KEY对应,区别是这里可以使用通配符同时订阅多种类型的消息。
参数 | 值 | 说明 |
---|---|---|
queue | - | 绑定的队列 |
exchange | - | 绑定的Exchange |
routing-key | - | 订阅的消息路由规则 |
no-wait | false | 该方法需要应答确认 |
在RabbitMQ的管理界面中,我们可以看到该队列与Exchange和routing-key的绑定关系
消息消费实现
使用 basic_consume
对消息进行消费的时候,需要注意下面参数
参数 | 值 | 说明 |
---|---|---|
queue | - | 消费的队列名称 |
consumer-tag | - | 消费者标识,留空即可 |
no_local | false | 如果设置了该字段,服务器将不会发布消息到 发布它的客户端 |
no_ack | false | 需要消费确认应答 |
exclusive | false | 排他访问,设置后只允许当前消费者访问该队列 |
nowait | false | 该方法需要应答确认 |
消费端在消费消息时,需要从消息中获取消息被消费的次数,以此判断该消息处理失败时重试还是发送到失败队列。
在消息发送到重试队列和失败队列时,我们在消息的headers中添加了一个名为x-orig-routing-key
的字段,该字段是实现消息重试的关键字段,由于我们的消息需要在不同的Exchange,Queue之间流转,为了避免消息在重新投递到主Exchange时,被所有的消费者队列重新消费,在重试过程中,我们将消息的routing-key修改为队列名称,直接投递给原始消费消息的队列。x-orig-routing-key
用于在之后能够重新获取到最开始的routing-key。
这里的重复消费是指 某个消息被两个消费方A和B消费了,其中A消费失败,B成功,这时候,消息由A消费者重新投递到主Exchange后,B消费队列也会获取到该消息,因此就会导致B消费者重复消费已经消费国的消息
本文实现延时重试,使用了三个重试Exchange,Exchange如果订阅特别多的话,Exchange的压力会非常大,因此在非常极端的情况下,消息大批量失败,且消息收发非常快,那么Exchange的性能可能会有问题。
本文是使用发布订阅实现延时重试的消息执行,也会有其他思路。
RabbitMQ 发布订阅-实现延时重试队列(参考)的更多相关文章
- RabbitMQ发布订阅实战-实现延时重试队列
RabbitMQ是一款使用Erlang开发的开源消息队列.本文假设读者对RabbitMQ是什么已经有了基本的了解,如果你还不知道它是什么以及可以用来做什么,建议先从官网的 RabbitMQ Tutor ...
- RabbitMQ 发布订阅持久化
RabbitMQ是一种重要的消息队列中间件,在生产环境中,稳定是第一考虑.RabbitMQ厂家也深知开发者的声音,稳定.可靠是第一考虑,为了消息传输的可靠性传输,RabbitMQ提供了多种途径的消息持 ...
- Spring Boot 实现 RabbitMQ 延迟消费和延迟重试队列
本文主要摘录自:详细介绍Spring Boot + RabbitMQ实现延迟队列 并增加了自己的一些理解,记录下来,以便日后查阅. 项目源码: spring-boot-rabbitmq-delay-q ...
- RabbitMQ 发布订阅
互联网公司对消息队列是深度使用者,因此需要我们了解消息队列的方方面面,良好的设计及深入的理解,更有利于我们对消息队列的规划. 当前我们使用消息队列中发现一些问题: 1.实际上是异步无返回远程调用,由发 ...
- .Net下RabbitMQ发布订阅模式实践
一.概念AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计.消息中间件主要用于组件之间的解耦,消息的发 ...
- RabbitMQ 发布/订阅
我们会做一些改变,就是把一个消息发给多个消费者,这种模式称之为发布/订阅(类似观察者模式). 为了验证这种模式,我们准备构建一个简单的日志系统.这个系统包含两类程序,一类程序发动日志,另一类程序接收和 ...
- 使用redis的发布订阅模式实现消息队列
配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://w ...
- 3.rabbitmq 发布/订阅
1. 发布者 #coding:utf8 import pika import json import sys message = ''.join(sys.argv[1:]) or "hell ...
- Kafka — 高吞吐量的分布式发布订阅消息系统【转】
1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有哪两个条件 ...
随机推荐
- nginx 配置简单网站项目(linux下)
1.新建html2与html3两个网站项目测试,而html是本身就有,记得到/etc/hosts 添加dns记录 2.修改nginx.conf文件 3.测试访问 中间用到一些nginx的命令,就不截图 ...
- 第四周读书笔记——读《我是一只IT小小鸟》有感
读<我是一只IT小小鸟>有感 这是邓老师倾力推荐的一本书.这本书的标题化用了我们耳熟能详的歌词,算是较有新意吧.更重点在于,这本书的作者不是哪一位大牛,而是一群刚刚走出校 ...
- pyhthon常用模块hashlib
python hashlib模块 一,hashlib模块主要用于加密,其中提供sha1,sha224,sha256,sha384,sha512,md5算法.常用的使用md5即可完成需求. 一,使用md ...
- python 之路初(一):pycharm 安装 和 环境配置 和 中文乱码问题
从健身和学习中我一体会到坚持的力量.想写写东西的想法已经好久了,就是不知道怎么开始.最近生活开始给我各种攻击和磨练,我从声嘶力竭到沉默到默默坚持自己,改变自己并总结告诉自己:少说多看,看破不说破,宁愿 ...
- Python3 下实现 腾讯人工智能API 调用
1.背景 a.鹅厂近期发布了自己的人工智能 api,包括身份证ocr.名片ocr.文本分析等一堆API,因为前期项目用到图形OCR,遂实现试用了一下,发现准确率还不错,放出来给大家共享一下. b.基于 ...
- JavaScript中数组的增删改查以及应用方式
数组的增加方法 1.push()方法向数组中末尾添加一个元素,原数组改变 var arr=[1,2,3,4]; var arr1=arr.push(6); console.log(arr);//打印出 ...
- Nginx主程序使用介绍
守护进程和服务 <br\>在首次运行Nginx之前,了解此应用程序的性质很重要. 有两种类型的计算机应用程序 – 那些需要用户输入,因此在前台运行,另一种在后台运行. Nginx是后一种类 ...
- Matplotlib常用绘图示例
一.Matplotlib介绍 Matplotlib是一个Python的2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形.通过Matplotlib,开发者可以仅需要几行代码,便可 ...
- 如何用命令行刷新,启用,禁用Magento2的缓存
当你使用Magento商店时如何刷新Magento 2中的Cache命令行是基本的常用操作.Magento 2默认有12种缓存类型.在命令行中有5个简单的命令来管理缓存.在这篇文章中,我将逐步向您展示 ...
- Kafka远程调试简单记录
Kafka启动脚本: ./kafka-server-start.sh -daemon ../config/server.properties 最终翻阅脚本可以确定是调用kafka-run-class. ...