小码收到猎头小姐姐的面试邀约后,认真进行了准备,并在约定时间到达了面试公司....
支付系统数据一致性问题在支付系统中数据的一致性问题是一个非常重要的问题,因为一旦发生数据不一致就意味着资金的损失,要么是用户支付了钱没有成功购买到商品;要么是平台没有收到用户的钱,却给用户错误地发送了支付成功的消息。无论哪种情况都会造成业务方对支付系统的不信任,因此我们要在整体系统流程上进行设计和考虑。从系统流程方向上看,支付系统数据不一致的发生主要有两个点:一是支付平台与第三方支付渠道(如支付宝、微信等)之间;二是支付平台与各个业务接入方之间。在支付系统建设中,所有的措施与手段都是围绕确保“业务接入方<->支付平台<->支付渠道”这三者之间的数据一致性而展开的!
分布式事务消息解决方案
即将讲到本文的重点,请大家抽根烟、保持耐心...
在实时流程中我们利用分布式事务消息主要针对的问题是在正常流程下,支付平台对第三方支付结果回调处理错误、以及支付平台对业务接入方回调逻辑错误的情况下而产生的支付状态数据不一致问题。
Rocket MQ延迟消息对账延迟消息对账的主要实现方式就是在向第三方渠道发起支付请求后,向Rocket MQ服务器指定队列发送一条延迟对账消息。例如可以设置30秒的延迟时间,30秒后Rocket MQ就会将消息真正投递到指定Topic中,处理实时对账的Consumer服务此时就会消费到延迟对账消息。
-
第三方回调已经完成,支付平台订单状态处于正确状态,此时逻辑不做任何处理,延迟消息被正常消费掉;
-
第三方回调未发生,支付平台订单状态未知,查询第三方渠道订单接口获取该笔支付的结果状态,如果为成功/失败,则更新支付平台订单状态完成回盘逻辑处理;
-
第三方回调未发生,支付平台订单状态未知,查询第三方渠道订单接口获取该笔支付结果状态,如果仍然是未支付,则将该延迟对账消息扔回MQ,并设置其延迟时间为2小时,2小时后再核查一次,如果仍然是未支付则表示用户无再次支付意愿,将订单状态置为失效;
无论是定时任务还是延迟消息,都需要一定的策略和逻辑来进行触发,试想一下如果需要触发的任务量非常巨大的话,需要轮训读取到期数据的耗时将会非常长。而且由于每个任务预设的时间长度不一样,轮训的频次如果都是一样的话,也会造成系统资源的浪费。
哈希轮算法
哈希轮又称“哈希时间轮”(Hash TimingWheel),在面临大量定时任务并且定时时长非常离散的任务系统中,哈希时间轮拥有非常强大的性能。从结构上看,哈希时间轮是一个存储定时任务的环形队列。如下图所示:
ts = (cs+(ti/si))%N
以上介绍的只是简单的哈希时间轮,在大多数实现中(例如Kafka)为了支持更多的场景还会使用多级时间轮的结构,不同的轮子可以采取不同的粒度,例如精度高的转一圈,精度低的仅往前移动一个槽!在具体实现哈希轮算法时可以根据实际应用场景设计不同的策略!
延迟消息在投递后延迟一段时间才对用户可见,如果要支持任意时长的延迟消息,假如支持30天,精度为1秒,按照之前的延迟等级划分,时间轮需要被分割成30*24*60*60=2,592,000个槽,如果支持的延迟时长天数更多,那么时间轮需要被分割的槽的个数还会增加,但是时间轮是需要被加载到内存中操作的,而这显然是不能被接受的。所以单个时间轮的方案是无法实现任意时长的延迟消息,因此需要构建支持时、分、秒的多级时间轮来解决。
但是在多级时间轮方案中,需要加载大量的数据到内存,这会造成比较大的内存开销,所以对于未来1小时或者未来一天的数据是可以不加载到内存的,通过延迟加载的方式只加载延迟时间临近的消息!
另外一个问题是在Rocket MQ中CommitLog是有时效性的,例如一般只会保存最近7天的数据,过期的数据将被删除。而对于延迟消息,可能需要在30天之后被投递,而这显然是不能被删除的数据,因此如果要在Rocket MQ中实现任意时长的延迟消息,还需要将延迟消息的存储从CommitLog剥离出来单独进行存储!
转载:https://blog.csdn.net/weixin_44296862/article/details/100589109