一次性付费做网站,怎么让付费网站免费,企业内部网页设计,手机app软件开发价格在阿里云栖开发者沙龙PHP技术专场上#xff0c;掌阅资深后端工程师、掘金小测《Redis深度历险》作者钱文品为大家介绍了RabbitMQ的延时队列和镜像队列的原理与实践#xff0c;重点比较了RabbitMQ提供的消息可靠与不可靠模式#xff0c;同时介绍了生产环境下如何使用RabbitMQ…在阿里云栖开发者沙龙PHP技术专场上掌阅资深后端工程师、掘金小测《Redis深度历险》作者钱文品为大家介绍了RabbitMQ的延时队列和镜像队列的原理与实践重点比较了RabbitMQ提供的消息可靠与不可靠模式同时介绍了生产环境下如何使用RabbitMQ实现集群间消息传输。
本次直播视频精彩回顾戳这里 直播回顾https://yq.aliyun.com/live/965 PPT分享https://yq.aliyun.com/download/3529
本文根据演讲视频以及PPT整理而成。
本文将主要围绕以下四个方面进行分享
RabbitMQ特性RabbitMQ中的消息不可靠问题及其解决方案死信队列生产环境下使用RabbitMQ应注意的事项
RabbitMQ特性 对于左边的Client Publisher而言RabbitMQ Server是消息的接收者也就是消费者;对于右边的Client Consumer而言RabbitMQ Server是消息的发送者也就是生产者。RabbitMQ Server将消息从Client Publisher传送给Client Consumer扮演着消息中间商的角色。 RabbitMQ Server负责将Client Publisher传递来的消息持久化延后地将消息传递给Client Consumer.这样即使消费者挂掉RabbitMQ Server也可以存储消息当消费者重新工作时再将存储的消息传递过去从而保证消息不丢失。RabbitMQ Server提供了堆积消息的能力。 另外RabbitMQ Server还具有复制和广播消息的能力。具体来说RabbitMQ Server可以将Client Publisher发布的消息分发给多个消费者比如它能够将特定的消息按照特定的队列分发给特定的消费者。“特定”指不同消息具有不同的routing key属性由上图实例不同的消息生产者生产了具有不同routing key的消息通过exchange路由器将不同的routing key消息投递到不同队列从而分发给不同消费者。
RabbitMQ中的消息不可靠问题及其解决方案
消费端消息不可靠问题及其解决方案 实际上RabbitMQ Server将消息投递给消费者具有消息不可靠的特点。具体来说RabbitMQ Server将消息投递给消费者时会调用套接字的write操作而write操作的过程是不可靠性的。在write操作的过程中Server需要将消息发送到套接字的缓存中通过网卡转发到链路上最终到达消费者所在的机器内核的套接字缓存中由消费者使用套接字的read操作将消息读出来。 即使套接字的write操作成功也无法保证消息可靠潜在的网络故障可能使消费者接收不到消息。机器宕机也可能使消息不可靠即使消息字节流已经到达消费者所在机器消费者所在机器的宕机也可能使消息无法被即时读取并处理。另外即使消费者即时读取消息内存消息队列中的所有消息也可能因为kill-9操作发生丢失。这些可能性都直接导致了消息不可靠。 因此需要额外的措施为消息提供可靠保障。一种消息可靠性保障方式是Server投递消息后并不立即将消息从Server删除而是等到消费者接收、处理消息并返回Ack包给Server后Server才删除该消息。如果消费者没有发送Ack包那么Server将重新投递该消息。这个过程确保消息被消费者处理保证了消息可靠。另外假如消费者已处理消息并发送Ack包给Server但由于网络故障等问题导致Ack包丢失时那么Server同样会重新投递该消息导致消息被重复处理。消息的重复处理通常由业务层面的技术手段来避免比如在数据库层面添加主键约束等。另一种重复消息处理的避免方式是客户端对每条消息维护ID, 将被处理消息的ID记录在列表中同时检查新到消息是否在该列表中。 RabbitMQ中的Auto Ack和Manual Ack对应着消息不可靠模式和消息可靠模式. Auto Ack即no ack,指消息投后即删除对应消息不可靠传输。Manual Ack即手动Ack,消费者处理完消息后使用Ack包通知Server删除消息对应消息可靠传输。 Auto Ack是RabbitMQ中最常用的模式性能较好但具有以下问题。当消息通过套接字write操作投递后RabbitMQ Server立即删除该消息该模式在遇到网络故障时容易发生消息丢失。另外假如消费者处理消息的速率过低可能导致消息在消费者recv buffer中大量堆积从而导致Server端send buffer也堆积大量消息, Server端无法继续调用套接字write操作。这样一段时间之后Server可能强制关闭消息传输链接导致消息不可传输。 尽管Auto Ack存在一定风险目前许多公司仍在应用Auto Ack模式。使用Auto Ack模式时开发者需要注意消费者和生产者的实例数量比例使消息生产者产生消息的速率与消费者消费消息的速率大致持平。 Manual Ack是RabbitMQ 中更加智能的一种模式。Manual Ack在工作时会考虑消息消费者的消息接收能力根据消费者的消息接受能力和当前接收到的Ack包自动调节分发消息的速率保证消息分发可靠、不阻塞。具体来说客户端通过PrefetchCount告知Server自身堆积消息的能力。 生产端消息不可靠问题及其解决方案 消息生产端同样存在消息的可靠性问题。从Client Publisher将消息传递给Server和从Server将消息传递给Client Consumer的过程是完全对等的Server和Client Consumer间传递消息的可靠性问题在Client Publisher和Server间同样存在。 Client Publisher首先将消息写到套接字再通过网络传递给Server的套接字buffer最终由Server读取该消息。这一过程的潜在网络问题也可能使Server端接收不到消息。 另外Server端本身也可能导致消息不可靠。Server端需要持久化消息但出于性能开销的考虑Server端并不在每次持久化消息时都刷盘。具体来说Server端会对文件执行write操作将脏数据写入操作系统的缓存中而不是立即将数据写入磁盘。一般情况下Server可能每几百毫秒执行一次fsync操作通过fsync操作将文件的脏数据写入磁盘。由于Server具有宕机风险那么每次Server宕机时还未被fsync操作处理的数据就可能丢失此过程类似于Redis AOF。 RabbitMQ通过生产者事务和生产者确认两个方法解决Server产生的数据不可靠问题。 生产者事务的基本原理是采用select和commit指令包裹publish在消息生产者publish数据之前执行select操作相当于begin transaction事务开始在执行若干个publish操作后再执行commit操作相当于提交事务。根据tcp包的有序性commit包成功接收意味着commit包之前的包也成功接收。因此收到从Client Publisher传递过来的commit包意味着该commit包之前的所有publish包都已成功接收即所有消息都成功接收。然而commit包只有等到Server端的fsync操作执行完毕时才返回因此生产者事务的效率较低通常只在有批量publish操作时才使用生产者事务模式。也就是说客户端将消息累计起来批量发送以降低fsync操作带来的性能损失。此外在进程中累计消息也存在风险累计的消息可能由于进程挂掉而丢失。总的来说生产者事务由于性能缺点不被RabbitMQ官方推荐。 另一种Server带来的数据不可靠问题的解决方案是生产者确认。生产者确认类似于消费端的Ack机制生产者可能连续发送多条消息Server将这些消息异步地通过fsync操作写入磁盘再异步地给生产者发送Ack包告知生产者消息的接收成功。由于Ack包异步传输不影响生产者端消息的正常发送。生产者确认模式下Ack包批量发送并且都携带有序号以告知生产者该序号以前的所有消息都已正常落盘。尽管RabbitMQ推荐用户使用生产者确认模式目前的RabbitMQ版本还未实现消息的重发机制只实现了Ack包的批量发送以通知Client Publisher哪些消息接收成功。当消息丢失时Client Publisher端已publish的消息在进程挂掉时也可能丢失而不是重新发送因此生产者确认的作用也不明显。当然生产者确认起到了降低消息发布速度的作用减小了消息丢失的数量。 生产者确认中的消息重发可以通过以下几种方法实现。第一种方式在内存中累积还未收到Ack包的消息收到Ack包后删除该消息对于一段时间内还停留在内存中的消息重发该消息。这种方式将未Ack消息存入内存一旦消息生产者宕机这些消息也会丢失。另一种方式将未收到Ack包消息存入磁盘当收到Ack包后删除该消息然而磁盘存储依赖于fsync操作降低了系统处理消息的性能。同时这还会提高编程的复杂度因为这要求发布消息时维护文件队列还要求一个异步线程将文件队列中的消息发布到Server带来了多线程和锁问题。还有一种方式将未Ack消息存入Redis但当出现网络故障时Redis也是不可靠的。目前提供的生产者确认中的消息重发方案都还存在问题具体的方案选择依赖于实际场景和个人取舍。
死信队列
生产者确认中的消息重发可以通过以下几种方法实现。第一种方式在内存中累积还未收到Ack包的消息收到Ack包后删除该消息对于一段时间内还停留在内存中的消息重发该消息。这种方式将未Ack消息存入内存一旦消息生产者宕机这些消息也会丢失。另一种方式将未收到Ack包消息存入磁盘当收到Ack包后删除该消息然而磁盘存储依赖于fsync操作降低了系统处理消息的性能。同时这还会提高编程的复杂度因为这要求发布消息时维护文件队列还要求一个异步线程将文件队列中的消息发布到Server带来了多线程和锁问题。还有一种方式将未Ack消息存入Redis但当出现网络故障时Redis也是不可靠的。目前提供的生产者确认中的消息重发方案都还存在问题具体的方案选择依赖于实际场景和个人取舍。 三、死信队列 死信队列使用了RabbitMQ中的一种特殊队列属性即x-message-ttl属性表示队列中消息的构建时间。假如用户在声明队列时定义队列的x-message-ttl属性此后所有进入该队列的消息都将持有构建时间到达构建时间的消息将被删除。如果还为队列配置了回收站属性那么即使构建时间到达RabbitMQ也不会立即删除这些消息而是将这些过期消息丢入回收站即死信队列。
死信队列的工作方式如上图。Client Publisher将消息投递给路由器也就是exchange,再由exchange将消息投递给队列由队列生成该消息的构建时间到达构建时间的消息将过期同时进入死信队列。过期消息进入死信队列的方式和进入普通队列的方式基本一致即先投递给exchange路由器再由exchange投递消息。消费者消费死信队列得到的消息是延后的消息延迟的时间长度即构建时间。目前死信队列存在的问题是一个队列只能设置一个构建时间消息的过期时间不够灵活不能满足一些特殊场景的需求比如动态的重试时间。 死信队列的另一个使用场景是Retry Later即在一段时间后才重新处理此前处理失败的消息这时可能用到双重死信。具体来说死信队列不仅可以接收过期消息还可以接收被reject的消息即消费端拒绝处理或处理过程发生异常的消息Reject操作具有requeue参数当requeue设为true时被reject消息会重新进入消息队列并被重新投递当requeue设为false时被reject消息将进入死信队列。假如死信队列持有构建时间那么到达构建消息的消息将重新投递给原有队列实现Retry Later。双重死信在使用过程中需注意消息处理的死循环问题因为消息可能无限循环地进入死信队列。
生产环境下使用RabbitMQ应注意的事项 生产环境下RabbitMQ通过使用集群模式。集群模式下只有元信息分布在所有节点中。元信息指队列信息路由器信息等队列中的信息只存储在一个节点中因此单个节点宕机会导致所有节点都不可用。另外RabbitMQ的所有节点间存在转发机制即允许节点转发其他目标节点的消息处理请求这样客户端只需连接到任意一个节点就可以实现其消息转发需求。 队列的高可用依赖于RabbitMQ的镜像队列即在其他节点上备份某节点的消息内容。这样当消息所在主节点宕机时其他镜像节点可以替代主节点完成消息传递任务。 通常情况下镜像节点是默默无闻的客户端无需感知镜像节点的存在。只有当主节点宕机时镜像节点才发挥作用。镜像队列的配置如下
Ha-mode具有三个选项all指将所有队列的信息存入所有节点这种模式最安全但也最浪费存储空间exactly指由用户精确指定每个队列的复制数当ha-mode设置为exactly,ha-params设置为2时表示“一主一从”这种模式是官方推荐的nodes指由用户指定副本所在的节点这种模式极少被使用。x-queue-master-locator用于设置存储队列主节点的RabbitMQ节点。min-master指将队列主节点设置在队列数量最少的RabbitMQ节点client-local指将队列主节点设置在当前客户端所在的RabbitMQ节点random即随机选择节点。Ha-sync-mode用于镜像节点代替宕机主节点并创建新节点以弥补缺失节点时设置新节点上数据的同步策略。automatic指自动地将新主节点上数据全部同步给新节点manual指不同步新主节点上的老数据只同步新产生的数据。由于节点间数据同步需要耗费时间长时间的数据同步可能会影响服务的稳定性但通常情况下RabbitMQ的节点堆积的数据量并不大因此RabbitMQ官方推荐使用Automatic进行数据同步。Ha-sync-batch-size指节点间批量同步的数据量。Ha-promote-on-shutdown表示主动停止主节点的服务时其他节点如何替代主节点。Always指其他节点总是能顺利地替代主节点when-synced要求与原主节点数据完全一致的节点才能替代主节点。Ha-promote-on-failure表示异常情况下其他节点如何替代主节点always和when-synced的含义与Ha-promote-on-shutdown中一致。许多公司为RabbitMQ集群设置了内存模式认为内存模式无需落盘能够提升系统性能。但实际上RabbitMQ官方文档指出内存模式无法提升系统性能它只提升了产生元信息数据的速度即Ram Node指将元信息存入内存可以提升元信息的创建速度而不是消息数据的性能。这是使用RabbitMQ时的一个常见误区。
原文链接 本文为云栖社区原创内容未经允许不得转载。