消息队列

消息队列的使用场景

参考文章:消息队列使用的四种场景介绍

1. 异步处理 / 最终一致性

非重要业务场景异步处理,提高接口响应速度。一个订单流程是非常长的,下单过程中需要各种校验、扣减商品库存、扣减服务者排班等,此时类似于下单短信或者微信模板消息等推送,就可以异步处理,将要发的短信内容和接收人信息等以MQ形式发出去,然后接收到短信后,再给用户发送。

允许非强一致性出现,主要保证最终一致性即可。

2. 应用解耦 / 消息广播

一对多:

订单完成时,可能积分系统需要给用户发积分,用户画像系统可能会给用户打上近期有完成单标签,优惠券系统可能需要给用户发券诱导再次下单等。此时对订单系统来说,将会有多个下游。此时订单系统就可以不关注下游信息,而将必要的订单信息通过MQ形式通知出去,相应的业务系统自己接受信息即可。

多对一:

用户画像系统需要实时记录用户定位城市,作为一种用户标签,服务于精准用户营销。此时,对于用户画像系统,是多端上游,上游比如有小程序、微信公众号、微信钱包、集团APP定位、业务线APP定位、外接纯H5定位等。而且对于这些多端来说,同步用户定位城市并非是它们的主流程,此时就可以由用户画像系统定义统一数据接收格式,多端按照此格式在定位成功后通知用户画像系统。

流量削峰

image.png-88.5kB
image.png-102.7kB

日志处理

日志处理是指将消息队列用在日志处理中,比如kafka的应用,解决大量日志传输的问题。架构图如下:

image.png-9.2kB

  • 日志采集客户端,负责日志数据采集,定时写受写入Kafka队列
  • Kafka消息队列,负责日志数据的接收,存储和转发
  • 日志处理应用:订阅并消费kafka队列中的日志数据

重试定时任务

此类主要是针对于延迟消息应用场景。在业务上遇到的场景是:支付成功后,需要回调业务线告知订单支付成功,业务线需要完成自己的订单以及按照自己的各种策略给商家结算,但是由于业务线处理流程非常长或者其他原因,时有回调失败的情况,且业务上不允许出现回调失败的情况,因为回调失败会影响到商家结算、商家账户收入等。但是回调失败的场景并不多见,平均一个月两三次。为了解决这个问题,就可以使用延迟消息队列,当回调失败时,先计算回调次数和回调延迟时间(这个延迟时间可以根据回调次数增大而增大),指定时间后接收到延迟消息再次给业务线尝试通知支付成功重试。

消息乱序解决思路

1. 丢弃旧消息

  • 适用场景:
    针对于只关注最终结果的用户场景。

  • 业务场景:
    营销部门需要接收用户打开APP的每次城市定位信息形成城市定位用户标签,此时对于营销部门只关心用户最后的城市定位消息。

  • 解决思路:
    强制发送方在消息体内加入一个时间戳,接收方接收到信息 处理成功后会落库。此时一条消息到达后,先验证是否有同一个用户但是时间戳更大的消息被消费成功了,如果有,则说明先发的消息延迟到达了,此时就可直接忽略时间戳更小的消息,因为我们只需要关注最新的消息即可。

2. 消息编排

以下单整个流程为例,下单消息、支付消息、退款消息是要求顺序到达的,不能退款消息在支付消息之前到达,此时业务无法处理。

  • 解决思路
  1. 按照业务先后顺序,人为消息编排:下单消息编号为1,支付消息为2,退款消息为3。
  2. 消息处理成功后,会保证落库成功。
  3. 一条消息到达后,会先检查前置消息是否消费成功(比如 支付的前置消息就是下单,退款的前置消息就是支付),只有前置消息消费成功后才进行本条信息消费。
  4. 如果前置消费根本就没有消费或者消费失败还在重试阶段,就先落库,然后ACK消息,不消费此条信息。
  5. 每条消息消费成功后,会先检查是否有后置消息需要消费,如果有则递归进行消费。

3. 消息间解耦

还是以下单流程为例,支付消息是可能会在下单消息之前到达,也就是消息间是耦合的,顺序是不能被打断。但是对于异步消息队列来说,我们无法保证顺序。

所以,我们可以在发送一条信息时带上全部前置消息的必要信息,比如退款消息,消息体中就可以带上必要的订单信息和支付信息;如果觉得这样消息体很大时,也可以只带上相应主键,比如订单id、支付记录Id等,然后反查补全消息消息时必须的信息。

消息的幂等性解决思路

可以由发送方在消息体内加上消息的一个全局唯一编号,接收方在接收到信息后,先使用分布式锁,以这个全局唯一编号作为key去加锁,加锁成功后,再验证是否有这条消息的成功消费记录,如果有则代表消息已被消费,直接ACK消息。如果没有则进行消息消费,消费成功后消息落库,释放锁。

posted @ 2018-03-24 10:55  做个有梦想的咸鱼  阅读(785)  评论(0编辑  收藏  举报