幂等性在实际应用的风险规避
幂等性相关的概率
幂等:是一个数学概念,表示N次变换和1次变换的结果相同。
幂等操作:其特点是任意多次执行所产生的影响均与一次执行的影响相同(不会改变资源状态,对数据没有副作用)。
幂等性:一系列操作都是幂等操作。
幂等接口:幂等接口认为,外部调用者会存在多次调用的场景,为了防止重试对数据状态的改变,需要将接口的设计为幂等的
总的来说幂等性是指一次和多次请求某一个资源应该具有同样的副作用。说白了就是,同一个请求,发送一次和发送N次效果是一样的! 实际场景举例
假设有一个人买东西需要付款去操作的一个场景,简化为方法为: boolean payGoods(account,monry);
payGoods的语义是从account的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。
与单体架构不同的是,在分布式环境中,通常会遇到复杂的问题,如请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了payGoods被调用两次,账户也被多扣了一次钱。
解决方案解决:幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如 : int create_ticket() bool payGoods(ticket_id, account, amount)
基于幂等性的解决方案在这种设计下,它对系统状态的影响可以忽略,任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。 什么情况下需要幂等
业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。在交易系统,支付系统这种重复提交造成的问题有尤其明显,比如: 用户在APP上连续点击了多次提交订单,后台应该只产生一个订单; 向支付宝发起支付请求,由于网络问题或系统BUG重发,支付宝应该只扣一次钱。 mq中的消息处理失败,重播进行消费处理
总之声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等! 实现幂等性的方案使用防重表,使用表的唯一索引,每次请求都根据表中插入一条数据,若数据存在,在次进行插入时候会失败 分布式锁,同一时间只能完成一次支付请求并做校验 token令牌,这种方式分成两个阶段:申请token阶段和支付阶段。第一阶段,在进入到提交订单页面之前,需要订单系统根据用户信息向支付系统发起一次申请token的请求,支付系统将token保存到Redis缓存中,为第二阶段支付使用。第二阶段,订单系统拿着申请到的token发起支付请求,支付系统会检查Redis中是否存在该token,如果存在,表示第一次发起支付请求,删除缓存中token后开始支付逻辑处理;如果缓存中不存在,表示非法请求。 扩展:幂等性在分布式事务中的应用
电商的很多业务,考虑更多的是 BASE(即Basically Available、Soft state、和Eventually consistent),而不是 ACID(Atomicity、Consistency、Isolation和 Durability)。即为了满足高负载的用户访问,我们可以容忍短暂的数据不一致。那怎么做呢?
第一,不做分布式事务,代价太大。
第二,不一定需要实时一致性,只需要保证最终的一致性即可。
第三,"通过状态机和严格的有序操作,来最大限度地降低不一致性"。
第四,最终一致性(Eventually Consistent)通过异步事件做到。
如果消息具有操作幂等性,也就是一个消息被应用多次与应用一次产生的效果是一样的话,那么把不需要同步执行的事务交给异步消息推送和订阅者集群来处理即可。假如消息处理失败,那么就消息重播,由于幂等性,应用多次也能产生正确的结果。