#java幂等性解决方式

需要幂等性的情况

1, 用户多次点击按钮
2, 用户页面回退再次提交
3, 微服务互相调用, 由于网路问题, 导致请求失败, feign触发重试机制

解决方式

1, token方式

类似于验证码一样, 只有接收的验证码, 和系统生成的验证码一致, 才进行操作
注:
需要使用脚本, 保证以下三个点的原子性

  • 获取token
  • 判断相等,
  • 删除token,
    是一个原子操作, 可以使用redis
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return  0 end

2, 锁机制

  • 数据库悲观锁
select * from xx where id = 1 for update;

注:
1, 悲观锁, 一般伴随事务一起使用, 数据锁定时间可能会很长, 需要根据实际情况使用;
2, id字段一定是主键或者唯一索引, 不然可能会造成锁表;

  • 数据库乐观锁
update biao set xx = 2 , version = version+1 where xx = 1 and version = 1

注:
1, 使用版本号进行操作

3, 分布式锁

4,唯一约束

  • 数据库唯一约束
    比如, 将订单号设置成唯一id, 保证只能插入一次
  • redis set防重
    将这条数据的MD5值放入redis的set, 每次处理完数据, 先看看这个MD5是否已经存在, 存在就不处理

5, 全局唯一ID

调用接口时, 生成一个唯一id, 比如重试的时候, 使用老请求的原来数据

6, 防重表

Token防重令牌

1, 服务器颁发令牌

String token = UUID.randomUUID().toString().replace("-", "");
//服务器自己保存
redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
//返回给前端
confirmVo.setOrderToken(token);

2, 前端提交请求的时候, 传递给后端防重令牌

3, 后台接收, 进行验证以及业务处理

  //1, 验证令牌[核心是: 令牌的对比和删除必须是原子性的]
        //因此使用lua脚本, 这个脚本只会返回0(失败) 或者 1(成功)
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return  0 end";
        String orderToken = vo.getOrderToken();
        //原子验证令牌和删除令牌
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()), orderToken);

        if(result == 0){
            //令牌验证失败
            
        }else{
            //令牌验证通过
        }

你可能感兴趣的:(java,java,数据库,lua)