首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 高并发系统:它的通用设计方法是什么?
02 | 架构分层:我们为什么一定要这么做?
03 | 系统设计目标(一):如何提升系统性能?
04 | 系统设计目标(二):系统怎样做到高可用?
05 | 系统设计目标(三):如何让系统易于扩展?
06 | 面试现场第一期:当问到组件实现原理时,面试官是在刁难你吗?
07 | 池化技术:如何减少频繁创建数据库连接的性能损耗?
08 | 数据库优化方案(一):查询请求增加时,如何做主从分离?
09 | 数据库优化方案(二):写入数据量增加时,如何实现分库分表?
10 | 发号器:如何保证分库分表后ID的全局唯一性?
11 | NoSQL:在高并发场景下,数据库和NoSQL如何做到互补?
12 | 缓存:数据库成为瓶颈后,动态数据的查询要如何加速?
13 | 缓存的使用姿势(一):如何选择缓存的读写策略?
14 | 缓存的使用姿势(二):缓存如何做到高可用?
15 | 缓存的使用姿势(三):缓存穿透了怎么办?
16 | CDN:静态资源如何加速?
17 | 消息队列:秒杀时如何处理每秒上万次的下单请求?
18 | 消息投递:如何保证消息仅仅被消费一次?
19 | 消息队列:如何降低消息队列系统中消息的延迟?
20 | 面试现场第二期:当问到项目经历时,面试官究竟想要了解什么?
21 | 系统架构:每秒1万次请求的系统要做服务化拆分吗?
22 | 微服务架构:微服务化后系统架构要如何改造?
23 | RPC框架:10万QPS下如何实现毫秒级的服务调用?
24 | 注册中心:分布式系统如何寻址?
25 | 分布式Trace:横跨几十个分布式组件的慢请求要如何排查?
26 | 负载均衡:怎样提升系统的横向扩展能力?
27 | API网关:系统的门面要如何做呢?
28 | 多机房部署:跨地域的分布式系统如何做?
29 | Service Mesh:如何屏蔽服务化系统的服务治理细节?
30 | 给系统加上眼睛:服务端监控要怎么做?
31 | 应用性能管理:用户的使用体验应该如何监控?
32 | 压力测试:怎样设计全链路压力测试平台?
33 | 配置管理:成千上万的配置项要如何管理?
34 | 降级熔断:如何屏蔽非核心系统故障的影响?
35 | 流量控制:高并发系统中我们如何操纵流量?
36 | 面试现场第三期:你要如何准备一场技术面试呢?
37 | 计数系统设计(一):面对海量数据的计数器要如何做?
38 | 计数系统设计(二):50万QPS下如何设计未读数系统?
39 | 信息流设计(一):通用信息流系统的推模式要如何做?
40 | 信息流设计(二):通用信息流系统的拉模式要如何做?
当前位置:
首页>>
技术小册>>
高并发系统设计核心
小册名称:高并发系统设计核心
### 18 | 消息投递:如何保证消息仅仅被消费一次? 在构建高并发系统时,消息队列(Message Queue)作为一种关键的中间件技术,被广泛用于解耦系统组件、异步处理任务、以及实现高效的负载均衡。然而,消息投递的可靠性,尤其是确保每条消息仅被消费一次(Exactly-Once Semantics),是设计这类系统时面临的重大挑战之一。本章节将深入探讨实现这一目标的多种策略、技术细节及其适用场景。 #### 一、引言 在高并发环境下,消息队列不仅是数据流动的管道,更是系统稳定性和数据一致性的关键保障。然而,由于网络延迟、系统故障、消费者处理异常等原因,消息可能会被重复投递、遗漏处理或处理多次。其中,确保消息仅被消费一次尤为重要,因为它直接关系到业务逻辑的准确性和数据的一致性。 #### 二、消息投递模式概览 在讨论如何保证消息仅被消费一次之前,有必要先了解消息队列常见的几种投递模式: 1. **至多一次(At-Most-Once)**:这是最不可靠的投递方式,消息可能因各种原因(如网络问题)而丢失,导致消费者未能接收到消息。 2. **至少一次(At-Least-Once)**:这是大多数消息队列默认的投递模式,确保消息至少被投递一次,但可能会因为网络重试、消费者处理失败后的重试机制等原因,导致消息被重复消费。 3. **恰好一次(Exactly-Once)**:这是最为理想但实现难度最大的投递模式,要求每条消息被且仅被消费一次。 #### 三、实现恰好一次投递的策略 ##### 3.1 幂等性设计 幂等性(Idempotence)是指无论执行多少次操作,结果都保持一致。在消息消费场景中,实现幂等性可以有效防止消息重复消费的问题。具体实现方式包括: - **唯一标识**:每条消息附带一个全局唯一的ID或序列号,消费者在处理消息前,先检查这个ID是否已被处理过。如果是,则直接忽略该消息。 - **业务状态检查**:在消费逻辑中加入对业务状态的检查,确保只有在特定条件下才执行消费操作。例如,更新数据库记录时,先检查当前记录状态是否符合更新条件。 ##### 3.2 事务消息 事务消息是一种将消息发送与本地事务处理绑定在一起的机制。它要求消息发送和本地事务操作要么同时成功,要么同时失败,从而确保数据的一致性和消息的可靠性。实现方式通常包括: - **两阶段提交(2PC)**:将消息发送分为准备阶段和提交阶段。在准备阶段,消息队列将消息暂存但不立即可见,同时本地事务开始执行。若本地事务成功,则消息队列将消息标记为可消费;若失败,则回滚消息。 - **本地消息表**:在数据库中维护一个本地消息表,用于记录消息的状态。本地事务处理成功后,再将消息发送到消息队列,并通过事务回滚或补偿机制确保消息与业务数据的一致性。 ##### 3.3 确认机制 确认机制是确保消息被正确消费的关键。大多数消息队列都支持消息确认(Acknowledge)功能,即消费者在处理完消息后,向消息队列发送确认信号,表明该消息已被成功消费。 - **自动确认与手动确认**:自动确认模式下,消息一旦被消费者接收,即视为已消费,这可能导致消息在消费过程中因异常而丢失。手动确认则允许消费者在处理完消息后,显式地向消息队列发送确认信号,从而更加灵活地控制消息的消费流程。 - **批量确认与个别确认**:为了提高效率,可以批量确认已处理的消息;但在某些场景下,为了精确追踪消息状态,可能需要对每个消息进行个别确认。 ##### 3.4 分布式锁 在分布式系统中,使用分布式锁可以有效避免消息被重复消费的问题。当消费者接收到消息时,先尝试获取一个与消息相关的分布式锁。如果获取成功,则进行消息处理;处理完成后释放锁。若获取锁失败,则说明有其他消费者正在处理该消息,当前消费者可以选择放弃处理或稍后重试。 然而,分布式锁的使用也带来了额外的复杂性和性能开销,需要谨慎选择适用场景。 #### 四、技术选型与最佳实践 在实现恰好一次投递的过程中,技术选型至关重要。不同的消息队列产品(如RabbitMQ、Kafka、RocketMQ等)在支持事务消息、幂等性设计、确认机制等方面存在差异。因此,在选择消息队列时,需根据系统需求、性能要求、运维成本等因素综合考虑。 此外,以下是一些最佳实践建议: - **明确业务需求**:在设计消息系统前,充分理解业务需求,明确消息投递的精确性要求。 - **测试验证**:在开发过程中,通过单元测试、集成测试等方式,验证消息投递的准确性和可靠性。 - **监控与日志**:建立完善的监控体系和日志记录机制,及时发现并处理消息投递过程中出现的问题。 - **容错与恢复**:设计合理的容错机制和恢复策略,确保在系统故障或异常情况下,消息投递的连续性和可靠性。 #### 五、总结 在高并发系统设计中,确保消息仅被消费一次是保障系统稳定性和数据一致性的重要环节。通过幂等性设计、事务消息、确认机制以及分布式锁等策略的综合运用,可以有效解决消息重复消费的问题。然而,这些策略并非孤立存在,而是需要根据具体场景和需求进行选择和组合。同时,技术选型、测试验证、监控与日志记录以及容错与恢复机制的建立也是确保消息投递可靠性的关键因素。
上一篇:
17 | 消息队列:秒杀时如何处理每秒上万次的下单请求?
下一篇:
19 | 消息队列:如何降低消息队列系统中消息的延迟?
该分类下的相关小册推荐:
大规模数据处理实战
shell脚本编程高手速成
Linux性能优化实战
从 0 开始学架构
构建可视化数据分析系统-ELK
etcd基础入门与实战
Redis入门到实战
RPC实战与核心原理
Web服务器Nginx详解
云计算那些事儿:从IaaS到PaaS进阶(五)
从零开始学大数据
Linux系统管理小册