分布式 幂等性和实现方式,幂等分布
墨初 知识笔记 139阅读
唯一主键完成幂等性时需要注意的是该主键一般来说并不是使用数据库中自增主键而是使用分布式 ID 充当主键这样才能能保证在分布式环境下 ID 的全局唯一性。

关键步骤需要生成全局唯一主键 ID
主要流程如下

客户端执行创建请求调用服务端接口。
服务端执行业务逻辑生成一个分布式 ID
将该 ID 充当待插入数据的主键然 后执数据插入操作运行对应的 SQL
语句。
服务端将该条数据插入数据库中如果插入成功则表示没有重复调用接口。如果抛出主键重复异常则表示数据库中已经存在该条记录返回错误信息到客户端。
数据库乐观锁实现幂等性数据库乐观锁方案一般只能适用于执行更新操作的过程我们可以提前在对应的数据表中多添加一个字段充当当前数据的版本标识。
这样每次对该数据库该表的这条数据执行更新时都会将该版本标识作为一个条件值为上次待更新数据中的版本标识的值。
关键步骤 需要数据库对应业务表中添加额外字段
为了每次执行更新时防止重复更新确定更新的一定是要更新的内容我们通常都会添加一个 version
字段记录当前的记录版本这样在更新时候将该值带上那么只要执行更新操作就能确定一定更新的是某个对应版本下的信息。
这样每次执行更新时候都要指定要更新的版本号如下操作就能准确更新 version5
的信息
UPDATE my_table SET priceprice50,versionversion1 WHERE id1 AND version5
上面 WHERE
后面跟着条件 id1 AND version5
被执行后id1
的 version
被更新为 6
所以如果重复执行该条 SQL 语句将不生效因为 id1 AND version5
的数据已经不存在这样就能保住更新的幂等多次更新对结果不会产生影响。
针对客户端连续点击或者调用方的超时重试等情况例如提交订单此种操作就可以用 Token
的机制实现防止重复提交。
简单的说就是调用方在调用接口的时候先向后端请求一个全局 IDToken
请求的时候携带这个全局 ID
一起请求Token
最好将其放到 Headers
中后端需要对这个 Token
作为 Key
用户信息作为 Value
到 Redis
中进行键值内容校验如果 Key
存在且 Value
匹配就执行删除命令然后正常执行后面的业务逻辑。如果不存在对应的 Key
或 Value
不匹配就返回重复执行的错误信息这样来保证幂等操作。
NOTE 获取Token只做一次而非每次调用资源请求时都获取。
获取一次TOKEN可以随页面加载的时候去获取或调用资源接口前获取一次。
在资源接口一次或多次请求时使用的token是一样的。如果刷新界面或切换资源id可以重新获取token
关键步骤
需要生成全局唯一 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
表达式来注销查询与删除操作。
所谓请求序列号其实就是每次向服务端请求时候附带一个短时间内唯一不重复的序列号该序列号可以是一个有序 ID
也可以是一个订单号一般由下游生成在调用上游服务端接口时附加该序列号和用于认证的 ID
。
当上游服务器收到请求信息后拿取该 序列号 和下游 认证ID 进行组合形成用于操作 Redis 的 Key
然后到 Redis 中查询是否存在对应的 Key
的键值对根据其结果
Key
作为 Redis 的键以下游关键信息作为存储的值例如下游商传递的一些业务逻辑信息将该键值对存储到 Redis 中 然后再正常执行对应的业务逻辑即可。 关键步骤
要求第三方传递唯一序列号
需要使用第三方组件 Redis 进行数据效验
下游服务生成分布式ID
作为序列号然后执行请求调用上游接口并附带唯一序列号与请求的认证凭据ID。上游服务进行安全效验检测下游传递的参数中是否存在序列号和凭据ID。上游服务到 Redis 中检测是否存在对应的序列号与认证ID组成的 Key
如果存在就抛出重复执行的异常信息然后响应下游对应的错误信息。如果不存在就以该序列号和认证ID组合作为 Key
以下游关键信息作为 Value
进而存储到 Redis 中然后正常执行接来来的业务逻辑。 “
上面步骤中插入数据到 Redis 一定要设置过期时间。这样能保证在这个时间范围内如果重复调用接口则能够进行判断识别。如果不设置过期时间很可能导致数据无限量的存入 Redis致使 Redis 不能正常工作。
实现 基于 防重Token令牌方案 实现代码仓库
无难事者若执 / Spring Utils · GitCode