【接口幂等】

什么是接口幂等呢?

  1. 接口幂等是指一个操作执行一次和执行多次的效果是一样的,那么这个操作就是幂等的。例如,在数据库操作中,删除一条记录的操作是幂等的,因为无论你执行多少次删除同一记录的操作,结果都是该记录被删除一次,查询接口也是幂等的,无论你查询多少次,查询操作也不会更改任何状态
  2. 在HTTP协议中,一些方法如GET和DELETE被认为是幂等的,而POST通常不是幂等的

GET:获取资源,可以安全地重复调用而不改变状态。

DELETE:删除资源,也是幂等的,删除一次和多次结果相同。

POST:通常用于创建资源,重复调用会创建多个资源,因此不是幂等的。

  1. 在分布式系统和微服务架构中,接口的幂等性尤其重要,它可以帮助确保在网络不稳定的情况下,重复请求不会导致数据不一致或者产生不必要的副作用。例如,在支付系统中,为了防止重复扣款,支付确认接口应该设计成幂等的,即使用户多次点击“确认支付”按钮,也只会扣费一次。

为什么需要接口幂等?

  1. 处理网络重试:在网络不稳定的情况下,客户端可能会因为没有收到响应而重新发送请求。如果服务器端实现了接口的幂等性,即使重复请求也能保证不会对系统状态造成额外的影响。
  2. 避免重复操作:在某些情况下,比如支付确认或数据库记录删除,操作应该是唯一的。如果这些操作不具备幂等性,可能会导致数据错误或资金损失。
  3. 简化客户端逻辑:如果一个接口是幂等的,客户端就不需要担心重复请求的问题,从而简化了客户端的设计和实现。
  4. 提高系统的健壮性:幂等性可以提高系统的健壮性,因为它减少了由于重复请求导致的数据一致性问题。
  5. 易于调试和维护:幂等性有助于简化系统的调试和维护工作。在出现问题时,可以更容易地追踪到问题的原因,而不是纠结于请求是否被重复执行了。
  6. 符合HTTP规范:HTTP协议中定义了一些方法为幂等的(如GET, HEAD, DELETE),遵循这些规范有助于构建更可靠的应用程序和服务。

什么情况下才需要接口幂等?

  1. 网络不稳定环境:
    当网络连接不可靠或存在高延迟时,客户端可能需要重发请求来确保操作成功完成。在这种情况下,幂等接口可以确保多次重发不会导致不良后果。
  2. 分布式系统和微服务架构:
    在分布式系统中,组件之间的通信可能存在失败或延迟。幂等接口可以确保即使在网络分区或故障恢复后重新发送请求,也不会影响最终的一致性。
  3. 涉及金钱交易的应用:
    在电子商务、在线支付等涉及财务的应用中,幂等性对于确保交易准确性和避免重复扣款非常重要。例如,支付确认接口必须是幂等的,以避免因网络延迟而导致的重复扣款。
  4. 修改数据的危险操作:
    对于那些可能会永久改变数据状态的操作(如删除记录、更新关键信息等),幂等性可以确保即使操作被执行多次,结果也是预期的,不会造成数据损坏。
  5. 批量操作或事务处理:
    在执行批量数据更新或事务处理时,幂等性可以确保即使部分操作失败并需要重试,整体操作仍然保持一致性。
  6. 并发场景下的操作:
    在多线程或多用户环境中,幂等性可以防止并发请求导致的数据冲突或不一致性。
  7. API设计的最佳实践:
    即使在上述情况之外,良好的API设计也应该考虑到幂等性作为最佳实践的一部分,以增强系统的可靠性和可预测性。
  8. 总的来说,任何需要确保操作在多次执行时结果一致的场景下,都需要考虑接口的幂等性。这不仅限于技术上的需求,也是提升用户体验、降低系统复杂度的重要手段。
  9. 如果确定重复执行不会带来额外副作用的情况下,也可以不实现接口的幂等的,比如禁用一个账号,只要接口中实现的就是改变为一个指定的状态,那么无论请求多少次,也是一个结果

接口幂等的实现方案有哪些?

  1. 利用数据库的唯一索引

唯一索引可以是单个字段,也可以是联合唯一索引(多个字段组合的唯一索引),原理也很简单,比如我们的系统中现在需要建立一个草稿模板,这个草稿模板是基于正式版本创建的,只能存在一个草稿版本,那么就可以根据联合唯一索引来保证,一旦命中了联合唯一索引,就认为已经存在一个对应的草稿版本了,无论你怎么请求,数据库都会因为唯一索引冲突报错,写入失败。

这里有个误区,就是我代码里去查一下DB然后校验一下不也行吗,在理想情况下是可以的,但是如果两次请求几乎是同一时间发起的,就可能导致A在查询的时候, 其实B已经在写入了,但是因为一些延迟导致A查询的时候发现没查到,A也写入了,由于没有唯一索引,就会出现两条,还有一种情况,比如你在定时任务里写了这样的判断来防止重复,但是由于项目部署的方式是水平扩展的分布式架构部署的,假设A和B和C三台机器,分别执行定时任务的时候,校验也可能出问题,因为他们处于不同的事务当中,而唯一索引不仅可以用来确保数据的唯一性,还可以在并发写入时起到锁的作用。这主要是因为在插入数据时,如果违反了唯一索引的约束,数据库管理系统(DBMS)会阻止该操作,并可能锁定相应的行或表,直到操作完成或回滚。这一机制可以有效防止并发插入导致的数据不一致。

  1. 利用数据库的乐观锁/悲观锁

乐观锁假设并发冲突较少,因此在读取数据时不加锁,而是通过版本号或时间戳来检测数据是否被其他事务修改过,比如在表中增加一个版本号字段 version,用于记录数据的版本。查询数据时,读取数据及其版本号。更新数据时,携带上查询到的版本号,检查数据库中的版本号是否发生变化。如果版本号发生变化,则说明数据已被其他事务修改,此时可以回滚事务或重新尝试更新。如果版本号没有变化,则正常更新数据,并将版本号递增。

悲观锁是假设并发冲突较多,因此在读取数据时立即加锁,直到事务完成后再释放锁。第一步在查询时语句后加上FOR UPDATE(排他锁),第二步进行写操作,第三步提交事务释放锁,简而言之可以理解为

1. SELECT * FROM TABLE WHERE XXX FOR UPDATE
2. UPDATE TABLE SET XXX WHERE XXX
3. COMMIT (锁会在事务结束时自动释放)

使用悲观锁一定要考虑好锁释放的问题,一旦一个事务长时间的占用锁,并且释放不了,将会导致其他的操作也阻塞了,在使用悲观锁的时候,尽量要保证最小粒度的数据影响,避免锁影响的数据量太大,也可以锁设置超时时间,一旦长时间不释放,主动回滚,并且把锁释放掉,同时也要加入系统监控,设置报警机制,及时发现问题,特别是在大批量数据操作的时候,悲观锁不是一个好的实现方案

  1. 利用Token机制

客户端在发送请求时生成一个唯一的Token(通常是UUID或者其他形式的唯一标识符)。
这个Token随请求一起发送到服务器端。服务器端接收到请求后,首先检查这个Token是否已经存在。如果Token不存在,则处理请求,并将Token存储在数据库、缓存或其他持久化存储中。
如果Token已经存在,则说明请求已经被处理过,直接返回之前的处理结果。如果请求是第一次处理,则执行相应的业务逻辑,并返回结果。如果请求已经被处理过,则直接返回之前的结果,避免重复处理。我们可以将Token放到缓存中间件中,比如Redis,利用缓存来提高性能

对应Token的过期时间不同场景设置的时间也不一样,拿支付操作来说,设置几分钟的过期时间就完全足够

  1. 利用分布式锁实现

利用分布式锁实现接口的幂等性是一种有效的方法,特别是在需要处理并发请求的场景下。分布式锁可以在多个节点之间协调并发操作,确保同一时间只有一个节点在处理特定的操作,实现方案常见的比如Redis和ZooKeeper [临时有序节点 (ephemeral sequential nodes), 当前创建的节点是否是最小的节点,如果是,则获取锁成功]

我们以Redis举例子,还是上面说的创建草稿,对于Redis的分布式锁来说一般我们利用Redis的Map结构来存储数据,Key 可以使用正式版本的主键 ID 作为key,例如 create_draft_a21312cawsd1231222, value 我们就存储为布尔类型,假设现在我们开始创建请求,首先我们拿key去查一下redis,如果是redis中查到了并且对应的value是ture,则认为已经有草稿版本了直接返回不继续操作了,如果为false则认为没有继续创建,创建完之后把value更新为true,当然如果后续草稿版本删除了,我们也要即使更新缓存,设置为false

接口幂等的实现需要注意什么?

  1. 唯一标识符的选择

确保所选的唯一标识符在整个系统范围内是唯一的。如果标识符不够唯一,可能会导致误判,进而引发错误

  1. 错误处理

无论用什么方式实现的接口幂等,在幂等校验返回异常的时候,都应该返回一个友好的提示,比如说唯一索引冲突返回的异常,不能说直接抛给前端Exception,而是应该捕获到异常,并且转为另一种友好的方式告诉给用户原因

  1. 性能影响

使用唯一标识符、数据库事务、分布式锁等机制可能会增加系统的性能开销,尤其是在高并发场景下。考虑使用缓存等机制来减轻性能负担。

  1. 数据一致性

确保在执行幂等操作时,所有相关的数据库操作都在同一个事务内完成,以保持数据一致性。特别是分布式架构,往往只靠本地的事务就保证不了一致性了,需要引入分布式事务来解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值