幂等性接口实现

什么是接口幂等性

  • 幂等性:在数学中某一元运算为幂等时,其作用在任一元素两次后会和其作用一次的结果相同。
  • 接口幂等性:它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外),即第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。

为什么需要实现接口幂等性

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

如何实现接口幂等性

  1. 数据库唯一主键
    利用数据库的唯一主键来保证“插入”操作的幂等性,需要注意的是如果在分布式环境下,需要使用分布式ID而不是数据库的自增ID。
  2. 数据库乐观锁
    数据库乐观锁方案一般只能适用于执行“更新操作”的过程,我们可以提前在对应的数据表中多添加一个字段,充当当前数据的版本标识。这样每次对该数据库该表的这条数据执行更新时,都会将该版本标识作为一个条件,值为上次待更新数据中的版本标识的值。
    在数据库中加入version字段,每次更新的时候,指定要更新的版本号。
UPDATE shop_item SET price=price+50,version=version+1 WHERE id=1 AND version=5
  1. 防重token令牌
    接口调用方在调用接口的时候先向后端请求一个全局ID(token),请求的时候携带这个token,后端对这个token进行验证,如果存在就删除该token并执行接下来的业务逻辑。如果不存在就返回重复执行的错误信息,以此来保证接口的幂等性。

创建与验证Token工具类

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TokenUtilService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 存入 Redis 的 Token 键的前缀
     */
    private static final String IDEMPOTENT_TOKEN_PREFIX = "idempotent_token:";

    /**
     * 创建 Token 存入 Redis,并返回该 Token
     *
     * @param value 用于辅助验证的 value 值
     * @return 生成的 Token 串
     */
    public String generateToken(String value) {
        // 实例化生成 ID 工具对象
        String token = UUID.randomUUID().toString();
        // 设置存入 Redis 的 Key
        String key = IDEMPOTENT_TOKEN_PREFIX + token;
        // 存储 Token 到 Redis,且设置过期时间为5分钟
        redisTemplate.opsForValue().set(key, value, 5, TimeUnit.MINUTES);
        // 返回 Token
        return token;
    }

    /**
     * 验证 Token 正确性
     *
     * @param token token 字符串
     * @param value value 存储在Redis中的辅助验证信息
     * @return 验证结果
     */
    public boolean validToken(String token, String value) {
        // 设置 Lua 脚本,其中 KEYS[1] 是 key,KEYS[2] 是 value
        String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        // 根据 Key 前缀拼接 Key
        String key = IDEMPOTENT_TOKEN_PREFIX + token;
        // 执行 Lua 脚本
        Long result = redisTemplate.execute(redisScript, Arrays.asList(key, value));
        // 根据返回结果判断是否成功成功匹配并删除 Redis 键值对,若果结果不为空和0,则验证通过
        if (result != null && result != 0L) {
            log.info("验证 token={},key={},value={} 成功", token, key, value);
            return true;
        }
        log.info("验证 token={},key={},value={} 失败", token, key, value);
        return false;
    }

}
幂等性指的是对同一个接口多次调用,结果是一样的,不会产生副作用。在实际场景中,比如网络不稳定、请求超时等情况下,可能会导致接口被重复调用,如果接口不具备幂等性,就有可能造成数据重复提交等问题。 为了保证接口幂等性,我们可以在接口中添加某些操作,比如在数据库中添加唯一约束、使用分布式锁等等。下面是一个使用 Spring Boot 和 Redis 实现接口幂等性的示例代码: ```java @RestController public class DemoController { @Autowired private RedisTemplate<String, String> redisTemplate; @PostMapping("/demo") public String demo(@RequestParam("id") String id) { String key = "demo:" + id; ValueOperations<String, String> opsForValue = redisTemplate.opsForValue(); Boolean absent = opsForValue.setIfAbsent(key, "true"); if (absent != null && absent) { // 执行业务逻辑 // ... redisTemplate.delete(key); return "success"; } return "fail"; } } ``` 在这个示例中,我们使用 Redis 来实现接口幂等性。当第一次请求接口时,我们使用 Redis 的 setIfAbsent 方法来设置一个键值对,如果设置成功,说明这个接口还没有被调用过,可以执行业务逻辑。执行完业务逻辑之后,我们再删除这个键值对,这样下次再请求同一个接口时,就不会重复执行业务逻辑了。 需要注意的是,接口幂等性实现不是一成不变的,具体实现方式需要根据业务场景进行调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VI仔爱学习

让我看看是谁在学习

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值