防止接口重复调用方法

1、使用数据库唯一索引:

为需要防重复的字段添加唯一索引,再尝试插入,如果重复会报错。

2、使用线程本地变量

利用ThreadLocal存储是否调用过的标识变量。

private static final ThreadLocal<Bolean> CALLED =new ThreadLocal<>();
if(CALLED.get() != null) {
  //已调用
} else {
  CALLED.set(true);
  //调用逻辑
}

3、使用synchronized同步块

private static final Object LOCK = new Object();
synchronized(LOCK) {
  if(isCalled) {
  //已调用  
  } else {
  isCalled = true;
  //调用逻辑
  }
}

4、使用AQS同步器实现锁

ReentrantLock、ReentrantReadWriteLock等。

5、使用全局变量加volatile

保证可见性,但不保证原子性。

6、使用数据库sequence生成唯一ID

避免业务重复调用。

7、redis锁。

防止重复调用的原理如下:

1、Redis锁

利用Redis的SETNX命令可以实现原子锁,保证同一时刻只有一个客户端能获取锁,其他客户端获取锁失败。通过设置过期时间实现锁的自动释放。

数据库唯一索引

为需要保证唯一的字段添加唯一索引约束。如果重复插入会报错,从而保证不重复执行业务逻辑。

2、ThreadLocal

ThreadLocal利用线程局部变量存储是否调用过的标记。由于每个线程都有自己的副本,可以保证同一线程内不重复调用。

3、同步锁

synchronized通过监视器对象(锁)实现同步,保证同一时刻只有一个线程持有锁执行同步代码块。其他线程获取锁失败被阻塞。

4、全局变量

利用volatile修饰全局变量,保证变量在不同线程之间的可见性。但不保证原子性,可能还是存在重复调用的风险。

5、序列号

使用数据库序列生成全局唯一的ID,保证业务执行时使用的ID不重复,从而避免重复调用。

总之,这些方法通过加锁、唯一约束等手段,都可以保证同一时刻只有一个线程/客户端执行需要防重复的业务逻辑,从而达到防重复调用的目的。

使用Redis的setkey和getkey方法防止重复调用有一定的问题:

1、setkey和getkey操作不是原子性的,存在竞争条件。多个线程同时调用setkey设置值,那么getkey可能拿到旧值,导致重复调用。

2、getkey只能判断值是否存在,无法实现锁功能。无法保证同一时刻只有一个线程执行业务逻辑。

3、没有设置key的过期时间,key会永久存在Redis中,不利于资源回收。

所以使用setkey和getkey防止重复调用存在以下潜在问题:

1、可能导致重复调用,无法保证原子性。

2、无法实现锁功能,无法保证同时只有一个线程执行。

3、key资源无法自动回收,可能会长期占用Redis存储空间。

相比之下,使用Redis的SETNX命令实现锁功能会更可靠一些:

1、SETNX是原子操作,可以保证同时只有一个客户端获取锁成功。

2、可以设置key的过期时间,锁资源可以自动释放。

3、锁可以实现同步控制,保证同时只有一个线程执行业务逻辑。

所以,如果要使用Redis防止重复调用,推荐使用SETNX实现分布式锁,而不是直接使用setkey和getkey。这可以更好地保证原子性和同步控制。

业务层代码:
//
分布式锁key String lockKey = ""; //防重复提交(通过Redis的setIfAbsent实现了分布式锁) if (!redisUtil.tryGetDistributeLock(lockKey, 2)) { //释放锁 redisUtil.releaseDistributeLock(lockKey); // 锁获取失败,直接返回 return new BaseModel(HttpUtil.failCode, "您的操作过于频繁,请稍后再试!", null); } Try{ }catch(){ }finally{ //释放锁 redisUtil.releaseDistributeLock(lockKey); }
redisutil: 
//
获取锁 public Boolean tryGetDistributeLock(String lockKey, int timeout) { return redisTemplate.opsForValue().setIfAbsent(lockKey, "1", timeout, TimeUnit.SECONDS); } //释放锁 public void releaseDistributeLock(String lockKey) { redisTemplate.delete(lockKey); }

 

posted @ 2023-11-30 15:46  噗噗噗i丶  阅读(94)  评论(0编辑  收藏  举报