浅谈Redis处理接口幂等性的两种方案

目录
  • 一、接口幂等性
    • 1.1、什么是接口幂等性
    • 1.2、为什么需要实现幂等性
    • 1.3、引入幂等性后对系统的影响
  • 二、如何设计幂等
    • 2.1、全局的唯一性ID
    • 2.2、幂等设计的基本流程
  • 三、接口幂等性常见解决方案
    • 3.1、下游传递唯一请求编号
    • 3.2、防重 Token 令牌
  • 参考链接:

前言:接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。对于一些用户请求,在某些情况下是可能重复发送的,如果是查询类操作并无大碍,但其中有些是涉及写入操作的,一旦重复了,可能会导致很严重的后果,例如交易的接口如果重复请求可能会重复下单。接口幂等性是指用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。

一、接口幂等性

1.1、什么是接口幂等性

在HTTP/1.1中,对幂等性进行了定义。它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果,即第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。这里的副作用是不会对结果产生破坏或者产生不可预料的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

这类问题多发于接口的:

  • insert操作,这种情况下多次请求,可能会产生重复数据。
  • update操作,如果只是单纯的更新数据,比如:update user set status=1 where id=1,是没有问题的。如果还有计算,比如:update user set status=status+1 where id=1,这种情况下多次请求,可能会导致数据错误。

1.2、为什么需要实现幂等性

在接口调用时一般情况下都能正常返回信息不会重复提交,不过在遇见以下情况时可以就会出现问题,如:

  • 前端重复提交表单: 在填写一些表格时候,用户填写完成提交,很多时候会因网络波动没有及时对用户做出提交成功响应,致使用户认为没有成功提交,然后一直点提交按钮,这时就会发生重复提交表单请求。
  • 用户恶意进行刷单: 例如在实现用户投票这种功能时,如果用户针对一个用户进行重复提交投票,这样会导致接口接收到用户重复提交的投票信息,这样会使投票结果与事实严重不符。
  • 接口超时重复提交: 很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络波动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。
  • 消息进行重复消费: 当使用 MQ 消息中间件时候,如果发生消息中间件出现错误未及时提交消费信息,导致发生重复消费。

本文讨论的是如何在服务端优雅地统一处理这种接口幂等性情况,如何禁止用户重复点击等客户端操作不在此次讨论范围。

1.3、引入幂等性后对系统的影响

幂等性是为了简化客户端逻辑处理,能放置重复提交等操作,但却增加了服务端的逻辑复杂性和成本,其主要是:

  • 把并行执行的功能改为串行执行,降低了执行效率。
  • 增加了额外控制幂等的业务逻辑,复杂化了业务功能;

所以在使用时候需要考虑是否引入幂等性的必要性,根据实际业务场景具体分析,除了业务上的特殊要求外,一般情况下不需要引入的接口幂等性。

二、如何设计幂等

幂等意味着一条请求的唯一性。不管是你哪个方案去设计幂等,都需要一个全局唯一的ID ,去标记这个请求是独一无二的。

  • 如果你是利用唯一索引控制幂等,那唯一索引是唯一的
  • 如果你是利用数据库主键控制幂等,那主键是唯一的
  • 如果你是悲观锁的方式,底层标记还是全局唯一的ID

2.1、全局的唯一性ID

全局唯一性ID,我们怎么去生成呢?你可以回想下,数据库主键Id怎么生成的呢?

是的,我们可以使用UUID,但是UUID的缺点比较明显,它字符串占用的空间比较大,生成的ID过于随机,可读性差,而且没有递增。

我们还可以使用雪花算法(Snowflake) 生成唯一性ID。

雪花算法是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs。这种算法由Twitter创建,并用于推文的ID。

一个Snowflake ID有64位。

  • 第1位:Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
  • 接下来前41位是时间戳,表示了自选定的时期以来的毫秒数。
  • 接下来的10位代表计算机ID,防止冲突。
  • 其余12位代表每台机器上生成ID的序列号,这允许在同一毫秒内创建多个Snowflake ID。

当然,全局唯一性的ID,还可以使用百度的Uidgenerator,或者美团的Leaf

2.2、幂等设计的基本流程

幂等处理的过程,说到底其实就是过滤一下已经收到的请求,当然,请求一定要有一个全局唯一的ID标记哈。然后,怎么判断请求是否之前收到过呢?把请求储存起来,收到请求时,先查下存储记录,记录存在就返回上次的结果,不存在就处理请求。

一般的幂等处理就是这样,如下:

三、接口幂等性常见解决方案

3.1、下游传递唯一请求编号

可能会想到的是,只要请求有唯一的请求编号,那么就能借用Redis做这个去重——只要这个唯一请求编号在Redis存在,证明处理过,那么就认为是重复的。

方案描述:

所谓唯一请求序列号,其实就是每次向服务端请求时候附带一个短时间内唯一不重复的序列号,该序列号可以是一个有序 ID,也可以是一个订单号,一般由下游生成,在调用上游服务端接口时附加该序列号和用于认证的 ID。

当上游服务器收到请求信息后拿取该 序列号 和下游 认证ID 进行组合,形成用于操作 Redis 的 Key,然后到 Redis 中查询是否存在对应的 Key 的键值对,根据其结果:

  • 如果存在,就说明已经对该下游的该序列号的请求进行了业务处理,这时可以直接响应重复请求的错误信息。
  • 如果不存在,就以该 Key 作为 Redis 的键,以下游关键信息作为存储的值(例如下游商传递的一些业务逻辑信息),将该键值对存储到 Redis 中 ,然后再正常执行对应的业务逻辑即可。

适用操作:

  • 插入操作
  • 更新操作
  • 删除操作

使用限制:

  • 要求第三方传递唯一序列号;
  • 需要使用第三方组件 Redis 进行数据效验;

主要流程:

主要步骤:

  • ① 下游服务生成分布式 ID 作为序列号,然后执行请求调用上游接口,并附带“唯一序列号”与请求的“认证凭据ID”。
  • ② 上游服务进行安全效验,检测下游传递的参数中是否存在“序列号”和“凭据ID”。
  • ③ 上游服务到 Redis 中检测是否存在对应的“序列号”与“认证ID”组成的 Key,如果存在就抛出重复执行的异常信息,然后响应下游对应的错误信息。如果不存在就以该“序列号”和“认证ID”组合作为 Key,以下游关键信息作为 Value,进而存储到 Redis 中,然后正常执行接来来的业务逻辑。

上面步骤中插入数据到 Redis 一定要设置过期时间。这样能保证在这个时间范围内,如果重复调用接口,则能够进行判断识别。如果不设置过期时间,很可能导致数据无限量的存入 Redis,致使 Redis 不能正常工作。

3.2、防重 Token 令牌

方案描述:

针对客户端连续点击或者调用方的超时重试等情况,例如提交订单,此种操作就可以用 Token 的机制实现防止重复提交。简单的说就是调用方在调用接口的时候先向后端请求一个全局 ID(Token),请求的时候携带这个全局 ID 一起请求(Token 最好将其放到 Headers 中),后端需要对这个 Token 作为 Key,用户信息作为 Value 到 Redis 中进行键值内容校验,如果 Key 存在且 Value 匹配就执行删除命令,然后正常执行后面的业务逻辑。如果不存在对应的 Key 或 Value 不匹配就返回重复执行的错误信息,这样来保证幂等操作。

使用限制:

  • 需要生成全局唯一 Token 串;
  • 需要使用第三方组件 Redis 进行数据效验;

主要流程:

① 服务端提供获取 Token 的接口,该 Token 可以是一个序列号,也可以是一个分布式 ID 或者 UUID 串。

② 客户端调用接口获取 Token,这时候服务端会生成一个 Token 串。

③ 然后将该串存入 Redis 数据库中,以该 Token 作为 Redis 的键(注意设置过期时间)。

④ 将 Token 返回到客户端,客户端拿到后应存到表单隐藏域中。

⑤ 客户端在执行提交表单时,把 Token 存入到 Headers 中,执行业务请求带上该 Headers。

⑥ 服务端接收到请求后从 Headers 中拿到 Token,然后根据 Token 到 Redis 中查找该 key 是否存在。

⑦ 服务端根据 Redis 中是否存该 key 进行判断,如果存在就将该 key 删除,然后正常执行业务逻辑。如果不存在就抛异常,返回重复提交的错误信息。

注意,在并发情况下,执行 Redis 查找数据与删除需要保证原子性,否则很可能在并发下无法保证幂等性。其实现方法可以使用分布式锁或者使用 Lua 表达式来注销查询与删除操作。

参考链接:

阿里面试官:接口的幂等性怎么设计?

优雅地处理重复请求(并发请求)

SpringBoot 接口幂等性实现的 4 种方案!这个我真的服气了!

到此这篇关于浅谈Redis处理接口幂等性的两种方案的文章就介绍到这了,更多相关Redis 接口幂等性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Springboot利用Redis实现接口幂等性拦截

    目录 前言 正文 实战开始 核心三件套 工具类三件套 REDIS配置类 前言 近期一个老项目出现了接口幂等性 校验问题,前端加了按钮置灰, 依然被人拉着接口参数一顿输出,还是重复调用了接口,小陈及时赶到现场,通过复制粘贴,完成了后端接口幂等性调用校验. 以前写过一篇关于接口简单限流防止重复调用的,但是跟该篇还是不一样的,该篇的角度是接口和参数整体一致才当做重复. 简单限流:Springboot使用redis实现接口Api限流的实例 该篇内容: 实现接口调用的幂等性校验 方案 :自定义注解+red

  • SpringBoot结合Redis实现接口幂等性的示例代码

    目录 介绍 实现过程 引入 maven 依赖 spring 配置文件写入 引入 Redis 自定义注解 token 的创建和实现 拦截器的配置 测试用例 介绍 幂等性的概念是,任意多次执行所产生的影响都与一次执行产生的影响相同,按照这个含义,最终的解释是对数据库的影响只能是一次性的,不能重复处理.手段如下 数据库建立唯一索引 token机制 悲观锁或者是乐观锁 先查询后判断 小小主要带你们介绍Redis实现自动幂等性.其原理如下图所示. 实现过程 引入 maven 依赖 <dependency>

  • 浅谈Redis处理接口幂等性的两种方案

    目录 一.接口幂等性 1.1.什么是接口幂等性 1.2.为什么需要实现幂等性 1.3.引入幂等性后对系统的影响 二.如何设计幂等 2.1.全局的唯一性ID 2.2.幂等设计的基本流程 三.接口幂等性常见解决方案 3.1.下游传递唯一请求编号 3.2.防重 Token 令牌 参考链接: 前言:接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题.对于一些用户请求,在某些情况下是可能重复发送的,如果是查询类操作并无大碍,但其中有些是涉及写入操作的,一旦重复了,可能会导致很严重的后果,例如交易

  • 浅谈Springboot实现拦截器的两种方式

    目录 一.拦截器方式 1.配置HandlerInterceptor 2.注册拦截器 3.使用拦截器的坑 二.过滤器方式 1.实现Filter接口 2.使用过滤器需要注意的 实现过滤请求有两种方式: 一种就是用拦截器,一种就是过滤器 拦截器相对来说比较专业,而过滤器虽然不专业但是也能完成基本的拦截请求要求. 一.拦截器方式 1.配置HandlerInterceptor 下面这个也是我们公司项目拦截器的写法,总体来说感觉还不错,我就记录了下来. 利用了一个静态Pattern变量存储不走拦截器的路径,

  • 浅谈Redis对于过期键的三种清除策略

    目录 Pre Redis Key的超时设置处理 被动删除 主动删除 当前已用内存超过maxmemory限定时,触发主动清理策略 对于过期键一般有三种删除策略 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作: 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键:如果没有过期,那就返回该键: 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键.至于删除多少过期

  • SpringBoot处理接口幂等性的两种方法详解

    目录 1. 接口幂等性实现方案梳理 1.1 基于 Token 1.2 基于请求参数校验 2. 基于请求参数的校验 在上周发布的 TienChin 项目视频中,我和大家一共梳理了六种幂等性解决方案,接口幂等性处理算是一个非常常见的需求了,我们在很多项目中其实都会遇到.今天我们来看看两种比较简单的实现思路. 1. 接口幂等性实现方案梳理 其实接口幂等性的实现方案还是蛮多的,我这里和小伙伴们分享两种比较常见的方案. 1.1 基于 Token 基于 Token 这种方案的实现思路很简单,整个流程分两步:

  • 浅谈Java实现分布式事务的三种方案

    一.问题描述 用户支付完成会将支付状态及订单状态保存在订单数据库中,由订单服务去维护订单数据库.由库存服务去维护库存数据库的信息.下图是系统结构图: 如何实现两个分布式服务(订单服务.库存服务)共同完成一件事即订单支付成功自动减库存,这里的关键是如何保证两个分布式服务的事务的一致性. 尝试解决上边的需求,在订单服务中远程调用减库存接口,伪代码如下: 订单支付结果通知方法{ ​ 更新支付表中支付状态为"成功". ​ 远程调用减库存接口减库存. } 上边的逻辑说明: 1.更新支付表状态为本

  • SpringBoot实现接口幂等性的4种方案

    一.什么是幂等性 幂等是一个数学与计算机学概念,在数学中某一元运算为幂等时,其作用在任一元素两次后会和其作用一次的结果相同. 在计算机中编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.幂等函数或幂等方法是指可以使用相同参数重复执行,并能获得相同结果的函数.这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变. 二.什么是接口幂等性 在HTTP/1.1中,对幂等性进行了定义.它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外),

  • 浅谈数据库缓存最终一致性的四种方案

    背景 缓存是软件开发中一个非常有用的概念,数据库缓存更是在项目中必然会遇到的场景.而缓存一致性的保证,更是在面试中被反复问到,这里进行一下总结,针对不同的要求,选择恰到好处的一致性方案. 缓存是什么 存储的速度是有区别的.缓存就是把低速存储的结果,临时保存在高速存储的技术. 如图所示,金字塔更上面的存储,可以作为下面存储的缓存. 我们本次的讨论,主要针对数据库缓存场景,将以redis作为mysql的缓存为案例来进行. 为什么需要缓存 存储如mysql通常支持完整的ACID特性,因为可靠性,持久性

  • 浅谈Java中实现深拷贝的两种方式—clone() & Serialized

    clone() 方法麻烦一些,需要将所有涉及到的类实现声明式接口 Cloneable,并覆盖Object类中的clone()方法,并设置作用域为public(这是为了其他类可以使用到该clone方法). 序列化的方法简单,需要将所有涉及到的类实现接口Serializable package b1ch06.clone; import java.io.Serializable; class Car implements Cloneable, Serializable { private String

  • 浅谈C#中Md5和Sha1两种加密方式

    1.新建控制台应用程序 2.新建类 EncryptHelper.cs public static class EncryptHelper { /// <summary> /// 基于Md5的自定义加密字符串方法:输入一个字符串,返回一个由32个字符组成的十六进制的哈希散列(字符串). /// </summary> /// <param name="str">要加密的字符串</param> /// <returns>加密后的十六

  • 浅谈Redis 缓存的三大问题及其解决方案

    目录 一.缓存穿透 1. 常见解决方案 2. 布隆过滤器 3. 缓存空数据与布隆过滤器的比较 二.缓存击穿 解决方案 三.缓存雪崩 解决方案 Redis 经常用于系统中的缓存,这样可以解决目前 IO 设备无法满足互联网应用海量的读写请求的问题. 一.缓存穿透 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起 id 为-1 的数据或者特别大的不存在的数据.有可能是黑客利用漏洞攻击从而去压垮应用的数据库. 1. 常见解决方案 对于缓存穿透问题,常见的解决方案有以下三种: 验证拦截:

随机推荐