• 详解分布式系统的幂等


    幂等是指一次和多次请求某一个资源应该具有同样的作用。

    什么是分布式的幂等

    首先,我们来设想以下几个场景:

    • 场景一:在 App 上确认订单的时候,点击多次没有反应,只能反复点击几次。在这种情况下,如果无法保证该接口的幂等性,那么将会出现重复下单的问题。

    • 场景二:在接收消息的时候,消息推送重复。如果处理消息的接口无法保证幂等,那么重复消费的消息影响可能会非常大。

    通常,为了满足高可用、高性能和高可扩展的特性,分布式系统拥有众多服务节点,但是却容易导致服务调用链冗长复杂,服务节点之间网络通信复杂度指数级增加,一处轻微网络故障就可能导致整个服务异常。

    同时,考虑到硬件设备自身故障的可能性,所以分布式系统面临一个共同的问题:一个消息(任务)可能会被重复消费。因此,如何确保同一个消息单次消费和多次重复消费具有相同的效果,也就是消息的幂等性成为一个热点问题

    其他常见的分布式解决方案

    1. 数据库添加唯一索引,比如订单号是唯一索引,防止生产重复订单;

    2. 使用分布式锁,防止应用程序出现并发操作;

    3. 采用 Token 机制,有效防止重复提交。提交后台时带 Token 值,需要先判断 Token 是否存在,若是存在,则删除 Token,执行业务逻辑;

    4. 数据库通过乐观锁;(update table_name set version=version+1 where version=0)

    5. 数据库悲观锁,通常是通过主键或唯一索引和事务一起实现。

    以上是其他常见的幂等解决方案,往往在不同的业务场景下会采用不同的方法。由于我们的主要业务是分布式存储,IO 从 Client 到 Server 的链路都不会很长,所以我们参考 Token 机制,实现了一套“新”机制来处理业务场景里存在的幂等问题。

    不同状态的消息处理

    如图所示,这是一个简单的任务系统,Server 成功执行了 ① 发送来的命令,但是并没有收到对应的结果。这是因为有网络因素的存储遇到问题,Client 都需要去重试任务,以排除网络不稳定带来的影响,但是在不同情况下,Server 收到重试消息时,需要有不同的应对方案。这时,可能会出现以下三种情况:

    第一种情况,Server 并没有收到对应的消息,因为有网络不稳定的情况存在,Client 是需要去重试的,这时 Server 会重新收到消息并且进行处理,正常处理之后返回对应的结果。

    第二种情况,Server 收到了对应的消息,还正在处理,由于其他因素导致任务执行时间过长。这是因为任务仍然在执行,所以为了保证幂等,我们需要等之前的任务执行完成后,再获取其执行的结果返回给客户端。

    第三种情况,Server 收到了对应的消息,并且已经处理完成,但是网络因素导致 Client  没有收到 ② 的结果,Server 收到重试的请求之后,直接获取已经收到结果返回给 Client。

    为了做到上面的功能,我们需要有一套合理的机制来保存已经收到的消息状态,并且需要在能够判断消息已经被 Client 收到结果时清理这个状态,这里我们引入了 SeqNumber 来做到这些事情。

    SeqNumber 的机制

    我们每次的请求都会带着一个之前已经完成的请求序号 ACK 和当前序号 Seq,Server 在处理时会根据 Seq 来判断当前消息的状态,保证一个 Seq 不会被执行多次。同时,会释放 ACK 对应的序号,因为只有在这个时候才能保证 Client 已经正确的完成了对应的请求。

    我们在 Client 端有两个队列 finished 和 running,分别存有正在执行的和已经完成的任务 ID。在 Server 端有一个 map,保存了所有未清理的消息(包括正在执行执行完成)。

    我们一个请求有以下几个步骤:

    1. 生成一个未使用的 Seq,如图在 request 里生成了 12;

    2. 将这个 Seq 插入 running 队列;

    3. 从已经完成的 finished 队列获取最小的 Seq 作为 ACK,如图 ACK=1,到这里 Client 将准备好的消息发送给 Server

    4. Server 收到请求,首先会将 ACK 对应的请求的缓存释放掉,然后获取对应 Seq 的消息,如果没有就说明是新的消息,直接插入,否则根据 map 之中的状态做对应的处理(对应到上文中三种状态的处理方式);

    5. 完成任务之后,返回结果给 Client,到这里 Server 已经完成了任务处理

    6. Client 收到了消息的结果,可以确认 Server 已经完成任务,这时候释放 finished 队列中 ACK=1 的项,然后将刚刚完成是 Seq=12 的任务移动到 finished 之中。

    总结

    分布式系统是一个非常庞大且复杂的系统,幂等只是其中非常重要且复杂的问题,在分布式系统的构建中,不同的系统对于幂等有着不同的需求,希望这篇文章能够对大家有所帮助。

  • 相关阅读:
    Sentinel底层原理(下)
    2022美亚杯电子数据取证大赛-个人赛
    【c/c++】cpp对c的函数增强
    面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了
    微信公众号留言如何实时提醒
    知识关联视角下金融证券知识图谱构建与相关股票发现
    通胀降温、比特币再探3.8W美元,金融巨头对经济持不同观点!
    web前端期末大作业——用HTML+CSS做一个漂亮简单的电影主题网站
    R语言并行计算提高速度丨parallel包和foreach包
    Python数据可视化:类别比较图表可视化
  • 原文地址:https://blog.csdn.net/YAN_RONG_TECHNOLOGY/article/details/126015514