关于MQ的使用,有一点想不明白

二手代码程序员
  • 22

在做用户注册的时候涉及到短信发送的服务,去网上查了查相关资料,发现大部分逻辑是这样的:
1.前端点击发送短信按钮
2.注册接口生成随机验证码缓存到Redis和MQ
3.短信接口从MQ获取信息并发送短信

这里我有一个疑问:
按照大多数逻辑来说当注册服务发送验证码以及手机号等信息给MQ之后应该已经返回给前台消息发送成功了,但是如果这个时候短信服务挂掉了或者说运营商那边出问题了,应该怎么办?而且MQ存在是为了解耦和异步,那这样并没有体现出来呀?

回复
阅读 1.9k
5 个回答

不考虑本地流程,短信在运营商那也是异步,不保证用户能收到,这里出错的原因可能很多,如:关机,欠费,无信号,用户电信黑名单,用户端软件拦截等等。发短信出错的环节不可胜数。从而基本上无法做到得到真正的发送成功的反馈。

发送成功了,仅是前端处理了,不意味着收到成功了。

怎么办?所以才都要设计各种重发的功能。

MQ为了解耦和异步?不已经是这样了吗?

有些消息队列是提供可靠性保证的,具体的技术细节可以网上找一下,一般是本地事务保证+重试+幂等接口,挺有意思的。

facelessman
  • 86

从 发送MQ 到 发送短信 是 3 个阶段:

发送消息 -> 消费消息 (调用发送短信接口) -> 运营商真正发送短信.

你有疑惑的地方是在消息消费逻辑这里.你这里其实只是调用了运营发送短信的接口, 告诉他你有条短信 (及短信内容) 要发. 他会把你这个要求也房东队列中, 然后按顺序发送. 至于他什么时候发, 能否发送成功, 而用户能否收到, 你就控制不了了.

所以你能做的就是尽量确保成功调用短信接口. 那么如何尽量确保呢?

以RocketMQ为例, 就是在消费消息时候,

  • 如果短信接口返回成功, 你的消费接口返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS,
  • 如果短信接口返回失败, 你的消费接口返回ConsumeConcurrentlyStatus.RECONSUME_LATER, 这样消息队列会自动重试, 触发你的消费逻辑, 直到成功或多次尝试还是最终失败. 最终失败后, 会记录消息到死信队列, 这个就需要手动处理了.

下面是消费者逻辑示例:

// 实例化消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test_consumer");

// 设置NameServer的地址
consumer.setNamesrvAddr("namesrvAddr");

// 订阅你的短新发送Topic
consumer.subscribe("sms_topic", "*");
// 注册回调实现类来处理从broker拉取回来的消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {

        // 这里调用短信发送接口
        boolean isSmsSentSucc = 发送短信接口();
        
        // 短信发送成功
        if (isSmsSentSucc) {
            // 标记该消息已经被成功消费
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        } else {
            // 标记等会继续尝试
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;
        }
    }
});
// 启动消费者实例
consumer.start();

这东西我觉得就是个见仁见智的事,根本也没有啥好纠结的。要我个人看,这个完全没必要搞异步,异步是为了把非必须同步的逻辑拆掉,提高响应速度的,但这个接口就是个发短信的接口,把发短信的逻辑变成异步,没有必要。但这么做了也没啥问题。至于准确性,同步其实也保证不了百分百准确。

吴金晓
  • 2
新手上路,请多包涵

首先解耦、异步是肯定了,看你的描述应该是将验证码生成和调用短信平台两块业务进行了异步处理,并且你不再直接调用短信平台业务了。
其次是必要性,这个是要看项目的情况,如果你的项目注册高峰值不高,而你的服务器集群又能满足响应速度,那肯定是简单点搞比较合适,因为你搞复杂了反而会增加问题。
最后是稳定性,运营商的问题咱肯定没法解决,如果短信服务出现异常,那么可以通过补偿等机制来保障一致,另外就算没出问题,也不能百分百保证一定能收到短信的。

宣传栏