接口的幂等性

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
    int value() default 2000;
}
@Aspect
@Component
@Slf4j
public class RedisRateLimiterAspect {

    @Autowired
    private RedisTemplate<String, Long> redisTemplate;

    @Pointcut("@annotation(com.zhkj.shopping.common.annotations.RateLimiter)")
    public void rateLimiterPointCut() {
    }

    @Around("rateLimiterPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);
        int millis = rateLimiter.value();
        UserWeb userWeb = ThreadLocalUtil.getInstance().getUserWeb();
        User user = ThreadLocalUtil.getInstance().getUser();
        String userKey = "";
        if (userWeb != null) {
            userKey = "userweb-" + userWeb.getId();
        } else if (user != null) {
            userKey = "user-" + user.getId();
        } else {
            throw new AppException("用户不存在");
        }

        String methodName = method.getName();
        String argString = Arrays.asList(joinPoint.getArgs()).toString();
        String key = userKey + methodName + argString;  //key主要用于对请求者,请求客户端的请求频次做限制,也可以使用token作为key
//        String key = method.getDeclaringClass().getName() + "#" + method.getName();

        Long expire = redisTemplate.opsForValue().getOperations().getExpire(key);
        if (expire != null && expire > 0) {
            throw new AppException("请求过于频繁,请稍后再试");
        }

        redisTemplate.opsForValue().set(key, System.currentTimeMillis(), millis, TimeUnit.MILLISECONDS);
        return joinPoint.proceed();
    }

}

关键是你的操作是不是幂等的!!!

前端做灰度  防止重复提交 

幂等性
1、查询操作:查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作;
2、删除操作:删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个) 
3、唯一索引和联合索引:防止新增脏数据。比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录。要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可);
4.token机制:防止页面重复提交。原理上通过session token来实现的(也可以通过redis来实现)。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。
下次客户端提交请求时,Token会随着表单一起提交到服务器端。服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。

5.悲观锁
获取数据的时候加锁获取。select * from table_xxx where id='xxx' for update; 注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的;悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用;

6、乐观锁——乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。乐观锁的实现方式多种多样可以通过version或者其他状态条件:
7.分布式锁如果是分布式系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,


查询的幂等性
查询操作天然是幂等的,同样的数据,无论你查询多少次,都不会对数据造成改变。

更新的幂等性
对于更新,可能存在重复更新的可能性,因此需要做其他的操作来保证幂等性,可以考虑使用数据version、行锁、分布式锁等来处理。

新增的幂等性
对于新增操作,存在重复新增的可能性,显然是非幂等性的,要保证幂等性,可以考虑在表中添加唯一索引或者联合唯一索引的方式来处理。

删除的幂等性
删除本身就是幂等性的,无论你删除多少次,参数相同的情况下,删除产生的结果都是相同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值