关于接口幂等性的设计

235 阅读2分钟

幂等

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中,即f(f(x)) = f(x) 简单的来说就是一个操作多次执行产生的结果与一次执行产生的结果一致

接口幂等

接口幂等,在请求接口的时候,有些需要保证接口在重复请求之后能产生的结果一致,不然会发生一些错误,比如

1、当用户注册时候,需要要保证用户账号的不可重复,当在同一时间以相同的账号注册,数据库可能就存在相同的账号,导致出错

2、下单支付回调时,业务逻辑没做幂等性,多次调用接口,导致库存不一致或者金额出错

3、多人购买商品,同时下单导致商品超卖,库存变为负数

常见解决方案

  • 唯一索引
  • token机制
  • 基于数据库锁(乐观锁和悲观锁)
  • 分布式锁(redis或zookeeper实现)
  • 状态机--状态变更,更新数据判断状态

在前面提到的例子中,用户注册的问题适合用唯一索引实现,在账号这个字段创建唯一约束,以相同的账号插入就会报错;下单支付回调这个例子适合状态机实现,因为回调是串行的,判断状态就能实现幂等性;多人购买这个这个场景,可以根据不同的需求使用基于数据库锁和分布式锁实现

token机制

  • 在每次提交请求前,都会生成一个唯一的token,并将其存储在redis或者ression中

  • 提交请求时,带上该token

  • 接口判断是否存在该token有则请求成功,否则返回访问失败

    该方式有效地防止用户重复提交数据,比如下单重复点击导致创建多条数据,评价和写文章多次提

redis+token 实现

生成一个token,并以字符串形式存储在redis中

    public String createToken() {
        RandomGUIDUtil guidUtil = new RandomGUIDUtil();
        String sessionId = guidUtil.valueAfterMD5;
        redisTemplate.opsForValue().set(sessionId,"1");
        return sessionId;
    }

检验时判断是否存在,存在则表明成功并删除该key

  public boolean checkToken(String sessionId) {
        Boolean flag = redisTemplate.hasKey(sessionId);
        if(flag){
            redisTemplate.delete(sessionId);
            return true;
        }
        return false;
    }

接口代码

    @ApiOperation(value = "校验token", notes = "获取token")
    @RequestMapping(value = "/checkToken.json", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 5000001, message = "参数错误")})
    public Result<String> checkToken(String token){
        boolean flag = tokenService.checkToken(token);
        if(flag){
            /**
             * 做业务逻辑等操作
             */
            return Result.jsonStringOk();
        }else{
            return Result.jsonStringError("请勿重新提交", ApiConstants.ERROR800);
        }
    }