《服务器开发技术、方法与实用解决方案》
幂等操作的特点是任意多次执行所产生的影响与一次执行的影响相同。
幂等函数是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数或方法不会影响系统状态,使用者无需担心重复执行会对系统造成改变
软件系统幂等的目标:
如系统A调用系统B服务,系统B调用系统C的服务超时而对A返回可重试失败。系统B通过定时任务对该笔业务进行重试,最终成功。系统A采用相同参数发起第二次调用,服务返回成功结果
随请求发送的具有唯一标识能力的字段,被称为幂等号,幂等号具有唯一性、不变性和传递性的特点
非业务幂等号:如UUID、时间戳。由于非业务幂等号难以通过业务上下文追溯,因此必须持久化,从而保证请求与幂等号的关系有迹可循
业务幂等号:由业务元素组合而成的幂等号,如用户ID+活动ID+商品ID。服务调用方可以不感知业务幂等号,被调用方根据请求参数及业务上下文获取所需参数拼接即可。
在真实环境中,任何一次请求的响应都存在不确定性,结果可能是执行成功、可重试异常、不可重试异常、超时异常等,为了确保重复请求得到正确的处理,还需要以下辅助信息:
在设计幂等处理流程前,应充分评估应用场景是否存在并发情况,若存在,则必须特殊处理。如在进入业务流程前,需先基于幂等号尝试获取全局锁
使用数据库的唯一索引,可以确保只会插入一条幂等记录。
具体步骤:
但是,该方式对于识别重复请求后的业务逻辑没有足够的保障能力。如插入幂等记录成功,但执行后续失败的情况。如果不回滚,是否支持重试;如果回滚,可能需要支持分布式事务
因此单独利用唯一索引只能应对非常简单的场景:除插入幂等记录外,不存在写操作;除插入幂等记录外,存在写操作,但可容忍数据不一致。在实际应用中,这样简单的场景非常少,因此唯一索引策略极少单独使用,大都是联合事务机制和锁机制使用
使用悲观锁+事务机制可实现业务幂等
如 select * from table_name where user_id = ‘xxx’ and biz_no = ‘yyy’ for update
悲观锁策略本质是通过将请求串行化来实现幂等,如果业务处理逻辑耗时较多,可能会导致大量线程长时间等待,浪费资源。
分布式锁也是通过将请求串行化来实现幂等。但分布式锁更加轻量,对获取锁失败的请求处理更加灵活。
在系统收到请求时,先尝试获取分布式锁,若获取成功,则继续执行业务逻辑;如获取失败,可舍弃请求直接返回,也可继续重试
对获取锁成功后的业务逻辑执行并没有可靠的保障。业务逻辑中可能涉及多次读写操作,任何一次操作都可能失败,从而衍生出数据一致性问题。因此在实际应用中,需要结合事务机制和重试机制才能形成完整方案。事务机制用于保证业务逻辑的数据一致性;重试机制则是基于持久化的幂等记录进行失败重试,保证最终一致性
实际应用中,幂等号可由客户端生成,也可由服务端生成。最显著的差别在于不变性,客户端因为不具备持久化能力,无法保证幂等号的不变性,一旦用户退出页面、关闭会话、重启客户端等,原来的幂等号就会丢失
通常采用Token机制来实现客户端与服务端之间操作的幂等性: