幂等问题。

文章讨论了接口幂等性的两种含义,重点介绍了业务幂等性,并提出了使用分布式锁和数据库唯一索引来确保数据一致性。此外,还阐述了一种防止重复提交的策略,通过生成和校验Token来避免多次提交。最后,提到了基于Redis的接口防刷机制,展示了如何限制接口在一定时间内的访问次数。
摘要由CSDN通过智能技术生成

       关于 接口的幂等问题  幂等这个问题 有2种意思 一种是接口的幂等 就是说执行一次与执行N次返回的结果都是一样的  我们一般常说的是业务的幂等 比如插入数据库 一旦插入成功那么不论再执行多少次插入 数据库中都只会存在一条记录

      为了解决业务的幂等问题 我们一般首先会使用分布式锁 锁的一般是此业务的唯一id 流水号之类的唯一值  加锁成功后然后 再对其进行业务的判断 判断数据库是否已经存在 并做出相应的操作 为什么这里需要用分布式锁呢 其实是为了防止2个线程同时 对这个业务进行读取操作 然后同时 进行写操作 造成的不安全现象  所以需要使用分布式锁  但是只使用分布式锁也不行 因为万一redis宕机 所以我们需要一个兜底的操作  所以在数据库层面我们需要对其进行加唯一索引  能防止重复的插入操作

除了上面的幂等  我们也可以做防止重复提交的操作 主要是首先需要在进入提交的这个页面之前生成一个token 存到redis当中 然后当点击提交时 携带上这个token 进行判断 当第一次提交时 判断是否一样  如果一样则进行删除 继续后面的逻辑   如果第一次点击时 携带这个token 判断不存在 则直接返回。

int count=0;
//防止重复提交
String orderId="11111111111";
String token=null;
String key="submit:"+orderId;
 token = UUID.randomUUID().toString().replaceAll("-", "");
 //放入redis
redisTemplate.opsForValue().set(key,token);
   while (true){
             String value= redisTemplate.opsForValue.get(key).toString();
       if (value!=null){
           //判断是否一样 一样则进行删除
           count++;
           System.out.println("第"+count+"次"+"不能重复提交");
           if (count==6){
               return;
           }
       }else{
           //因为这里 判断 与删除 是2个操作 所以防止并发不安全 所以使用lua脚本进行操作
           String script="if redis.call('get',KEYS[1])==ARGV[1] then redis.call('del',KEYS[1]) else return 0 end";
           DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>();
           longDefaultRedisScript.setScriptText(script);
           redisTemplate.execute(longDefaultRedisScript,Arrays.asList(key),token);
           count++;
           System.out.println("第"+count+"次"+"执行操作");
       }


   }

 

上面的做法都是基于redis的 redis 也可以做简易的防刷接口  使用aop+redis 当需要在一个接口上闲置访问的次数时 可以通过一个自定义的注解 加上aop redis 来进行限制 

如何 如果实现一个接口防止1秒内重复3次

首先自定义一个注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RepiApi {

    String time() default "1";

    String nums()default "3";
}

@Component
@Aspect
public class RepititionAspect {
    
    @Resource
    private RedisTemplate redisTemplate;
    
    @Around("@annotation(com.example.springbootredis.annotation.RepiApi))")
    public Object pointCut(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        String url = request.getRequestURI();
        MethodSignature signature =(MethodSignature)joinPoint.getSignature();
        RepiApi annotation = signature.getMethod().getAnnotation(RepiApi.class);
        String nums = annotation.nums();
        String time = annotation.time();
        String hostAddress = InetAddress.getLocalHost().getHostAddress();
        String key=hostAddress+":"+url;

        ValueOperations valueOperations = redisTemplate.opsForValue();
        String value = valueOperations.get(key).toString();
        if (value!=null){
            if (Integer.parseInt(value)>=Integer.parseInt(nums)){  System.out.println("不允许访问");
            }else{
                valueOperations.increment(key);
                return joinPoint.proceed();
            }

        }
        valueOperations.set(key,1,Long.parseLong(time), TimeUnit.SECONDS);
          return joinPoint.proceed();
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值