mq要如何处理消息丢失、重复消费?

Wesley13
• 阅读 445

如果要你实现一个支付宝向余额宝转账的功能,比如:账户a从支付宝转出5000余额宝转入5000,该怎么做呢?

可能有些人会说,这还不简单,直接上图

mq要如何处理消息丢失、重复消费?

支付宝先给账户a减5000,调用余额宝的接口给余额宝的账号b加5000。

用这种方式正常情况下是可以的,如果出现以下问题该怎么办呢?

  1. 调用余额宝api时网络失败了

  2. 调用余额宝api时网络超时了

  3. 如果余额宝api业务逻辑比较复杂,耗时比较长,用户需要长时间的等待才有结果,用户体验不好

有人说:如果调用余额宝api时网络失败了,对接口进行重试不就可以解决问题了。

:你是用同步重试,还是异步重试呢?

       如果用同步重试,即在调用余额宝api时获取返回值,如果发现失败立刻重试3次。调用一次余额宝api的耗时为n秒,重试3次的耗时则为3n秒,接口响应时间增加了两倍,增加了接口超时的风险。如果重试3次之后,还是失败该怎么处理?

        如果用异步重试,第一次调用余额宝api时,不管是成功还是失败,都直接给用户返回成功。如果是失败,后台开启一个线程,不断重试一直到成功为止。如果在不断重试的过程中服务器重启了,该怎么办?

又有人说:如果调用余额宝api时网络超时了,不知道上次请求是成功还是失败,再重试一下不行吗?

:不是不,第一.余额宝必须做幂等性设计,不然余额宝这边多转入5000怎么办?余额宝肯定不会犯这种错误。第二.同样会面临如果调用余额宝api时网络失败了的问题。

再有人说:如果余额宝api业务逻辑比较复杂,耗时比较长,用户需要长时间的等待才有结果,用户体验不好。改成异步就可以解决这个问题了。

答:改成异步可以提前告知用户结果,然后在后台通过补偿机制不断的重试,让数据达成最终一致性,这种方式对用户体验可能确实要好一些。异步处理又分为:开启线程  和 使用mq。线程处理有比较致命的弊端,如果服务器重启,线程里的数据会丢失。

接下来,我们的重点放在mq上。

mq要如何处理消息丢失、重复消费?

余额宝给账户a减了5000之后,给指定topic1发一条消息,然后余额宝从topic1消费这条消息,给账户b加5000。

对于问题1,如果余额宝处理失败了,比如像rocketmq这类消息处理框架会把消息放入重试队列重试16次,不需要业务代码做额外的工作。

对于问题2,如果服务器重启了,由于消息保存在服务端的磁盘上,不会丢失,客户端可以通过offset从服务端重新获取消息,它能够保证消息至少被余额宝消费一次。

对于问题3,支付宝给账户a减了5000发送完消息之后,可以直接返回成功,然后余额宝作为消费者在后台默默执行,一直到成功为止。

那么问题又来了:

如果余额宝消费了消息,业务处理失败了怎么办?这个就是所谓的消息丢失。

要解决消息丢失就需要建一张消息发送表,如图:

mq要如何处理消息丢失、重复消费?

       支付宝从账户a减5000,接着往本地消息表中写入一条消息记录,confirm_status为待确认,然后发送mq消息。注意,支付宝这边的扣款和写本地消息表要在同一事务中。

       余额宝消费消息给账户b加5000之后,调用支付宝消息确认api,修改confirm_status为已确认。

        如果余额宝这边消息丢失了,支付宝有个job会每个5分钟扫描一次本地消息表中confirm_status为待确认状态的记录,重新发送一次消息,这样余额宝又可以重新处理了。

那么还有个问题:

余额宝这边处理成功,但是由于调用 支付宝消息确认api失败,导致支付宝的job重新发送消息,余额宝重复消费了。这个就是所谓的重复消息。

重复消费要如何解决呢?

mq要如何处理消息丢失、重复消费?

        余额宝也增加一个本地消息表,记录业务处理成功的消息。当然余额宝的账号操作和本地消息表也要在同一个事务中。    

        余额宝消费消息之后,先从余额宝的本地消息表中查一下,该消息有没有消费过,如果已经消费过了,则直接调用支付宝消息确认api,修改confirm_status为已确认,避免下次支付宝的job重复发消息。如果从余额宝的本地消息表中查到没有消费,则给账户b增加5000,同时往本地消息表写一条记录,然后调用支付宝消息确认api。

       总结:通过在mq的生产者和消费者两端分别增加本地消息表,并且在生成者端增加定时job扫描待确认状态的记录,重新发送消息,可以解决:消息丢失重复消费 问题。当然实际的支付宝向余额宝转账的场景更复杂,在高并发的情况下,可能需要用分布式锁,防止金额异常。

本文分享自微信公众号 - 苏三说技术(gh_9f551dfec941)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KaliTools说明书+BurpSuit实战指南+SQL注入知识库+国外渗透报告
!(https://oscimg.oschina.net/oscnet/d1c876a571bb41a7942dd9752f68632e.gif"15254461546.gif")0X00KaliLinux Tools中文说明书!(https://oscimg.oschina.net/oscnet/
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这