
RabbitMQ 消息队列异步通信、可靠性全套解决方案在短信项目里上万条批量营销短信不会直接同步调用第三方通道而是先丢进 MQ消费者慢慢消费防止瞬间流量打垮服务。RabbitMQ 核心组成组件1.生产者 Producer业务服务负责发送消息。在项目里就是营销活动模块、定时短信任务模块批量生成短信任务后作为生产者发送消息。2.Broker 服务节点RabbitMQ 服务本体内部包含 Exchange交换机、Queue队列。3.Exchange 交换机消息的路由中转站生产者不会直接把消息发到队列先发给交换机交换机根据路由规则把消息分发到对应队列。 交换机 4 种类型Direct精准匹配路由键一对一分发短信业务普通下发队列使用Topic模糊匹配路由键用来区分不同短信渠道、不同营销活动Fanout广播模式一条消息同步发给所有绑定队列用于平台通知、告警推送Headers基于消息头匹配项目极少使用。4.Binding 绑定关系交换机和队列之间的绑定绑定的时候指定 routingKey 路由键决定消息分发规则。5.Queue 消息队列存储消息的容器消费者从队列拉取消息。队列支持持久化、设置最大长度、消息过期时间。6.消费者 Consumer监听队列、处理消息的服务项目中专门的短信推送消费服务读取消息后调用第三方短信通道完成下发。7.VirtualHost 虚拟主机MQ 内的环境隔离机制不同业务线、开发 / 测试环境分开互不干扰项目开发环境单独一套 vhost。三大核心设计作用异步、解耦、削峰RabbitMQ 是消息中间件核心作用异步解耦、流量削峰、最终一致性。流量削峰营销活动、节日大促会一次性生成几十万条短信任务如果同步循环调用第三方通道瞬间大量请求打满服务线程池、第三方接口超限封禁。MQ 可以把瞬时海量消息缓冲在队列中消费者匀速慢慢消费抹平流量高峰。系统解耦短信下发逻辑和活动创建逻辑完全分离。活动模块只负责生产消息不用关心短信下发是否成功、通道是否故障后续更换短信渠道、新增风控校验只改消费端不用改动活动创建代码。异步通信提升接口响应速度运营后台批量创建上万条短信任务同步执行会接口超时丢入 MQ 后接口直接返回成功消息后台异步处理前端不用长时间等待。消息可靠性底层机制防止消息丢失核心全套原理线上三大核心故障消息丢失、重复消费、消息堆积配套死信队列做兜底处理。1.消息丢失消息丢失分三个链路每个链路都有独立保障机制缺一不可链路 1生产者 → Broker 之间丢失消息场景生产者发送消息过程中服务宕机、网络中断消息没到达 Broker 就消失。底层机制生产者确认机制 Publisher Confirms开启 confirm 模式每条消息发送成功后Broker 会返回 ack 确认如果返回 nack 或者长时间无响应生产者本地重试发送或者记录本地日志定时补发。配套操作发送消息时设置持久化标识 deliveryMode2。链路 2Broker 内部丢失消息场景MQ 服务器断电、重启内存中未落地磁盘的消息全部清空。底层机制交换机持久化 队列持久化 消息持久化交换机持久化重启后交换机不消失队列持久化重启后队列不消失消息持久化消息写入磁盘而非仅存内存。 三者同时配置才能保证 Broker 重启消息不丢失。链路 3Broker → 消费者 丢失消息场景消息推送给消费者消费者还没处理完程序宕机消息直接被删除。底层机制消费者手动 ACK 确认机制自动 ACK默认消息一推送给消费者Broker 立刻删除消息风险极高线上禁用手动 ACK消费者完整处理完业务逻辑短信下发成功、日志入库手动发送 ack 指令Broker 才删除消息处理失败发送 nack 指令消息重新放回队列重试多次失败转入死信队列。2.重复消费问题完整原理产生根源消费者业务处理成功短信已经下发完成但是网络波动ACK 指令没能传递到 Broker。 Broker 收不到 ack认为这条消息没有处理完成一段时间后重新投递导致同一条短信下发两次造成用户收到重复短信。底层解决方案消费幂等每条短信任务生成全局唯一 messageId存入 Redis 做幂等标记。消费逻辑流程拿到消息先提取 messageId查询 Redis判断该 ID 是否已消费已存在直接 ACK 丢弃不执行下发不存在执行短信下发下发成功后写入 Redis 缓存再发送 ack。3.消息堆积问题底层原理产生原因生产速度 消费速度批量营销活动瞬间生成几十万消息生产者速度极快消费者处理慢调用第三方短信通道有网络延迟、数据库写入慢消费者数量过少单节点处理能力有限消费逻辑阻塞、频繁重试拖慢消费效率。堆积带来的危害队列消息过多占用服务器内存MQ 服务卡顿、崩溃新消息无法写入生产者阻塞报错消息长期积压过期失效业务数据丢失。分层解决方案横向扩容增加消费者实例分摊消息处理压力队列拆分按渠道、活动拆分多个独立队列避免单一队列消息过载限制队列最大长度超长消息直接转入死信优化消费内部逻辑减少同步 IO、批量操作数据库、异步附属逻辑。4.死信队列 DLX 完整机制什么消息会进入死信队列消息重试达到最大次数依旧消费失败消息过期队列长度超限新消息被丢弃转入死信。项目落地多级重试 死信架构业务队列消费失败先转入短期重试队列等待 1 分钟重试重试 3 次仍失败转入长期重试队列间隔 5 分钟重试两轮重试全部失败投递至死信队列后台定时任务监听死信队列统一记录异常短信运营人工排查黑名单、通道额度、手机号错误等问题。价值失败消息不阻塞正常业务队列统一归档排查不丢失异常数据。核心问题解决方案对比表格故障场景底层产生原因全套落地解决方案短信平台消息丢失生产者无确认、队列未持久化、自动 ACK1. 开启 Publisher-Confirm 生产者确认2. 交换机、队列、消息三层持久化3. 消费者手动 ACK重复消费业务处理成功ACK 网络丢失Broker 重发消息每条消息生成唯一 messageId消费前查询 Redis 校验实现幂等拦截重复下发消息堆积批量活动瞬时大量消息消费处理速度跟不上生产1. 扩容消费者节点2. 按渠道拆分多队列3. 优化消费内部 IO 逻辑4. 设置队列最大长度超限转死信消费持续失败黑名单手机号、通道欠费、号码格式错误多级延迟重试队列重试耗尽转入死信队列后台统一统计异常短信短信 MQ 完整业务流程流程图流程文字简化口述版运营创建营销活动 / 定时短信任务生产者生成唯一 messageId 封装消息开启生产者确认、消息持久化发送到 Topic 交换机按渠道路由分到对应业务队列消费者手动 ACK 拉取消息先查 Redis 校验 messageId 实现幂等重复消息直接丢弃校验通过后执行黑名单、频次风控校验失败直接进入重试队列风控通过调用第三方通道下发下发成功记录幂等标识、写入日志手动 ACK 删除消息下发失败则多次延迟重试重试耗尽转入死信队列定时任务读取死信消息归档至 ES运营统一查看异常短信。Nacos 注册中心 配置中心Nacos 整体定位Nacos 是阿里开源的微服务组件同时提供两大核心能力服务注册发现、动态配置管理完全替代 Eureka Spring Cloud Config项目微服务体系核心底座。在短信平台作用所有后端微服务营销活动服务、短信推送服务、风控服务、AI 文案服务统一注册到 Nacos第三方短信渠道密钥、营销限流阈值、QLExpress 规则开关、AI 模型密钥全部统一托管在配置中心。服务注册与发现AP 架构核心概念服务提供者短信推送服务、风控服务等业务服务启动时向 Nacos 上报自身 IP、端口、服务名服务消费者营销后台服务需要调用短信推送接口时从 Nacos 拉取所有可用服务实例健康检测Nacos 定时发送心跳长时间无心跳的服务实例自动剔除不会转发请求到故障节点。AP 架构特性服务注册选用 APCAP 理论中AP 代表高可用 分区容错牺牲强一致性。为什么注册中心选 AP营销高峰期哪怕短暂数据不一致也不能让服务注册功能瘫痪多节点集群一台 Nacos 宕机其余节点仍能正常提供注册、查询服务保证业务不中断。项目落地场景短信平台集群部署多台推送服务流量负载均衡分发某一台推送服务宕机Nacos 自动剔除实例Feign 远程调用不会路由到故障节点避免大量短信下发失败。动态配置中心CP 架构核心功能集中管理项目所有配置不用分散在每个服务 yml 文件支持配置分环境隔离开发 / 测试 / 生产、配置分组区分业务模块。关键注解RefreshScope配置修改后无需重启服务Spring Bean 自动刷新配置实时生效。CP 架构特性配置中心选用 CPCP 代表强一致性 分区容错牺牲部分可用性。配置渠道密钥、额度、风控规则属于核心敏感数据必须保证所有服务读取到的配置完全一致不允许出现 A 服务读取旧配置、B 服务读取新配置的情况因此采用 CP 模式保证数据统一。项目落地配置内容第三方短信渠道账号、API 密钥、请求地址短信下发限流阈值、单日用户最大发送次数QLExpress 规则引擎开关、黑白名单拦截阈值SpringAI 大模型接口地址、Token 消耗上限、AI 降级开关Seata、Sentinel 中间件参数。注册中心 vs 配置中心 核心区别维度Nacos 注册中心APNacos 配置中心CP核心职责管理微服务实例实现远程调用负载均衡统一管理项目所有业务、中间件配置CAP 选型AP优先保证高可用CP优先保证数据强一致更新方式服务心跳自动上报实例状态后台手动修改配置推送变更事件故障影响单节点宕机集群仍可正常查询服务集群半数节点不可用暂时无法修改配置业务价值微服务远程调用不路由故障机器修改渠道密钥、风控规则不用重启服务RefreshScope 底层简单原理Nacos 配置变更后推送事件给服务Spring 监听器捕获配置变更刷新对应作用域下的 Bean带有RefreshScope注解的类会重新从配置中心读取最新参数无注解 Bean 不会自动刷新必须重启服务才能加载新配置。Nacos 服务注册与发现流程服务注册流程短信推送、风控等微服务启动后注册到 Nacos持续上报心跳营销服务调远程接口时从 Nacos 拉取健康实例做负载均衡服务宕机心跳停止Nacos 自动剔除不会转发请求。Nacos 配置动态刷新流程配置刷新流程后台修改渠道密钥、限流规则等配置Nacos 推送变更消息给所有服务程序通过 RefreshScope 刷新 Bean直接读取新配置不用重启服务。Elasticsearc业务背景早期千万级短信下发日志存在 MySQL多条件检索、按手机号 / 活动 / 时间范围查询很慢后面迁移 ES 做日志检索MySQL 只存核心业务主数据ES 是什么核心定位Elasticsearch 是分布式全文检索引擎底层基于 Lucene 封装。核心能力全文检索、多维条件筛选、海量数据近实时查询。⚠️重点区分ES不适合高频事务写入不替代 MySQL适合海量日志、报表、检索类查询。在短信平台职责存储海量短信下发日志支撑运营后台按手机号、活动 ID、下发状态、时间范围多维度追溯短信记录。核心底层倒排索引正向索引MySQL 的存储思路文档 ID → 内容例1 号日志手机号 138xxxx状态成功2026-08-15 下发2 号日志手机号 139xxxx状态失败2026-08-15 下发如果查「所有失败短信」MySQL 要全表扫描 / 走索引数据量大之后很慢。倒排索引ES 核心词条 → 文档 ID 列表把字段内容拆成词条建立词条和文档的映射示例词条【下发失败】→ [2 号日志]词条【138xxxx】→ [1 号日志]词条【139xxxx】→ [2 号日志]优势检索时直接根据词条找到对应的文档不用遍历全表海量数据下多条件查询速度远快 MySQL。补充短信日志不会做分词手机号、状态这类字段我们设置为keyword精确匹配不分词。ES 基础核心概念ES 概念对标 MySQL 概念短信项目举例Index索引Database 数据库sms_log_index 短信日志索引Type旧版7.x 后废弃Table 表7.x 不再使用了解即可Document 文档Row 一行数据单条短信下发记录一条日志Field 字段Column 列手机号、活动 ID、下发状态、通道、创建时间Mapping 映射Table 表结构字段类型定义手机号 keyword、时间 date、消息内容 textShard 分片分库分表大索引拆分多个分片分布式存储水平扩容Replica 副本数据备份分片副本节点宕机不丢数据、保证查询可用版本提醒生产一般 ES7已经移除 Type写入与检索底层简单流程短信日志场景1写入流程短信下发成功后同步 / 异步写入 ES 日志短信消费端下发完成 → 组装短信日志文档 → 写入 ES文档先写入内存缓冲区Index Buffer同时写 translog 事务日志宕机恢复用定时刷新 refresh缓冲区生成段文件 segment进入文件缓存近实时可查默认 1s段文件不断合并merge最终刷入磁盘关键点ES 不是写入立刻磁盘持久化translog 保障宕机不丢refresh 默认 1s所以叫近实时检索不是强实时。2检索流程运营后台查询短信记录运营传入条件手机号 时间范围 下发状态协调节点接收查询请求路由到对应分片在分片内基于倒排索引快速匹配符合条件的文档各个分片结果汇总、排序、分页返回给业务服务为什么千万级短信日志要从 MySQL 迁移 ES短信日志属于海量、只查询、很少修改的数据写入量大不会更新MySQL 大表多条件联合查询B 树索引效率急剧下降业务经常不定条件检索手机号、活动、状态、通道、时间自由组合MySQL 很难提前建好所有联合索引索引过多会严重拖慢写入ES 倒排索引天然适合多维检索灵活组合条件查询性能稳定冷热分离MySQL 只保留近期核心业务数据历史海量短信日志归档 ES减轻 MySQL 压力。限制点ES 不适合强事务、高频更新场景所以核心业务数据商户、活动任务依旧放 MySQL。流程图短信日志写入 ES 流程运营后台检索短信日志流程MyBatis-PlusMyBatis-Plus 是什么MyBatis-Plus简称 MP是 MyBatis 的增强工具只增强、不修改完全兼容原生 MyBatis。核心目标减少基础 CRUD 重复代码不用手写简单的 Insert、Update、Delete、基础 SQL。在短信平台里商户信息、营销活动任务、黑名单、渠道配置这类 MySQL 业务表全部使用 MP 开发。核心常用能力1.通用 CRUD 封装BaseMapper内置 selectById、insert、updateById、deleteById单表基础操作不用写 XML。2.Lambda 查询构造器LambdaQueryWrapper、LambdaUpdateWrapper用 Java 实体类的方法引用避免硬编码字段名好处编译期就能校验字段字段改名直接编译报错防止手写字符串字段名出错比如 phone 手敲成 tel3.分页插件内置分页能力配置分页插件后直接selectPage自动拼接分页 SQL不用手写 limit。4.主键策略、自动填充比如创建时间、更新时间TableField(fill FieldFill.INSERT_UPDATE)自动填充不用每次 set。LambdaQueryWrapper 通俗举例短信项目场景需求查询某个活动下、手机号在黑名单之外、状态为启用的营销任务原生 MyBatis写 XML手写字段字符串容易写错字段MP Lambda 写法直接引用实体方法SmsActivity::getActivityId、SmsActivity::getStatus编译阶段校验字段改名直接报错线上不会出现因为字段名写错导致查不出数据。MP 适用边界✅ 适合单表简单 CRUD、单表分页、简单条件筛选活动、商户、黑名单这类单表业务❌ 不适合复杂多表联查、复杂统计、自定义复杂 SQL规范复杂 join、复杂统计我们仍然写原生 MyBatis XMLMP 只用来简化单表操作。