🚀 在高并发场景下基于MQ实现可靠短信发送机制

🚀 在高并发场景下基于MQ实现可靠短信发送机制

作者:天天摸鱼的java工程师 · 8年经验

标签:Java · RocketMQ/Kafka · 高并发 · 消息重试 · 架构设计 · 分布式可靠性


📌 背景场景

在实际的业务系统中,短信通知是一个非常常见的非核心但又关键的功能。例如:

  • 用户注册/登录时发送验证码
  • 订单支付成功通知用户
  • 活动营销、优惠券提醒等

短信服务属于强依赖外部服务的非确定性操作,可能因为第三方网关不稳定、网络抖动、限流等原因导致失败。如果你把短信发送逻辑直接写在业务流程内,那么一旦短信服务挂了,整个业务链就会受到影响,这是非常不可靠的。


🧠 设计目标

我们希望实现一个具备以下能力的短信发送系统:

  1. 异步解耦:业务系统不被短信服务影响;
  2. 可靠消息投递:短信失败后支持自动重试;
  3. 高并发支撑:能承受百万级并发消息流;
  4. 可观测性强:方便排查失败消息、查看状态。

🏗️ 架构设计

整体架构如下图:

css 复制代码
[业务服务] ---> [MQ Topic: sms-topic] ---> [短信消费者服务] ---> [短信网关]
                          ↑
                    [失败消息重试队列]

消息流转说明:

  1. 业务服务将短信消息发送到 MQ(如 RocketMQ/Kafka)
  2. 短信消费者异步消费消息,调用短信网关接口发送;
  3. 若发送失败(如第三方接口异常),将消息重新投递到延迟队列或 DeadLetter Queue;
  4. 系统根据重试策略进行补发,最大重试次数后仍失败则记录日志/人工告警。

🧪 核心代码实现(基于 RocketMQ)

1. 消息生产者发送短信任务

java 复制代码
public class SmsProducer {

    private final DefaultMQProducer producer;

    public SmsProducer(String producerGroup) throws MQClientException {
        producer = new DefaultMQProducer(producerGroup);
        producer.setNamesrvAddr("localhost:9876");
        producer.start();
    }

    public void sendSmsMessage(SmsMessage smsMessage) throws Exception {
        Message message = new Message("sms-topic", 
            smsMessage.getBizType(), 
            JSONObject.toJSONString(smsMessage).getBytes(StandardCharsets.UTF_8));
        message.setDelayTimeLevel(0); // 立即发送

        SendResult result = producer.send(message);
        log.info("消息发送成功:{}", result);
    }
}

2. 消费者接收并发送短信

typescript 复制代码
@RocketMQMessageListener(topic = "sms-topic", consumerGroup = "sms-consumer")
public class SmsConsumer implements RocketMQListener<String> {

    @Autowired
    private SmsGatewayClient smsGateway;

    @Autowired
    private RetryProducer retryProducer;

    @Override
    public void onMessage(String messageStr) {
        SmsMessage sms = JSONObject.parseObject(messageStr, SmsMessage.class);
        try {
            boolean success = smsGateway.send(sms);
            if (!success) {
                throw new RuntimeException("短信发送失败");
            }
        } catch (Exception e) {
            log.error("短信发送异常:{}", e.getMessage());
            retryProducer.sendRetryMessage(sms); // 发送到重试队列
        }
    }
}

3. 重试队列逻辑(延迟队列)

java 复制代码
public class RetryProducer {

    private final DefaultMQProducer retryProducer;

    public RetryProducer() throws MQClientException {
        retryProducer = new DefaultMQProducer("sms-retry-group");
        retryProducer.setNamesrvAddr("localhost:9876");
        retryProducer.start();
    }

    public void sendRetryMessage(SmsMessage smsMessage) throws Exception {
        if (smsMessage.getRetryCount() >= 3) {
            log.warn("短信重试超过最大次数,记录日志告警:{}", smsMessage);
            return;
        }

        smsMessage.setRetryCount(smsMessage.getRetryCount() + 1);
        Message message = new Message("sms-topic", 
            smsMessage.getBizType(), 
            JSONObject.toJSONString(smsMessage).getBytes(StandardCharsets.UTF_8));

        message.setDelayTimeLevel(3); // 延迟10s再重试(RocketMQ内置的延迟等级)
        retryProducer.send(message);
    }
}

⚙️ 高并发处理建议

1. 业务层异步解耦

使用 MQ 完全脱离主业务链路,业务响应时间更短:

scss 复制代码
// 接口响应无需等待短信发送完成
smsProducer.sendSmsMessageAsync(smsMessage);

2. 消费者水平扩展

RocketMQ/Kafka 都支持 多个消费者实例并发消费,通过部署多个实例来提升吞吐量。

yaml 复制代码
replicaCount: 5  # Kubernetes部署多个副本消费短信队列

3. 短信网关异步并发发送

如果使用第三方 SDK,可以使用线程池进行异步发送:

ini 复制代码
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> smsGateway.send(sms));

📈 可观测性与监控

  • 失败消息入库:记录失败原因、重试次数、最终状态;
  • Prometheus + Grafana:监控短信发送成功率、MQ堆积情况;
  • 报警机制:短信失败率超过阈值自动报警;

✅ 总结

通过 MQ 解耦和异步处理机制,我们不仅提升了系统的 稳定性和可用性 ,还具备了应对 高并发、可重试、可观测 的能力。经验告诉我们:

  • 外部服务一定要隔离,不能影响主业务链路
  • 一定要有重试机制和失败告警
  • 系统越大,越要注重每一环的可靠性设计
相关推荐
梦未3 小时前
Spring控制反转与依赖注入
java·后端·spring
喜欢流萤吖~3 小时前
Lambda 表达式
java
无限大63 小时前
验证码对抗史
后端
ZouZou老师4 小时前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式
用户2190326527354 小时前
Java后端必须的Docker 部署 Redis 集群完整指南
linux·后端
曼巴UE54 小时前
UE5 C++ 动态多播
java·开发语言
VX:Fegn08954 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
程序员鱼皮4 小时前
刚刚,IDEA 免费版发布!终于不用破解了
java·程序员·jetbrains