Redisson分布式锁

一,场景说明

为什么需要分布式锁呢?

  • 如果是单机服务,即只有一台服务器,那就不需要分布式锁,在代码中加锁就可以满足需求了。
  • 分布式环境下,服务都是部署在多台机器,普通锁已经无法满足需求了。以下面这个场景为例:

有一个创建人员信息的模块,前端页面中输入人员相关信息,点击“保存”按钮发送请求,后端收到请求后开始处理,生成主键ID及其它信息,安全起见可以在保存人员信息方法上加一个synchronized锁或者是ReentrantLock锁,最后将人员信息保存到数据库中。假如:前端没有控制好,第一次点击“保存”按钮后,并没有将按钮置为无效,客户点了两次“保存”按钮,发送了两次请求,这样会怎么样?

  • 上述场景如果是在单机环境下,是没有问题的,因为加了锁,即使发送两次请求,第二次请求也会被阻塞,等第一次请求执行完后才会执行,代码中再加入一些判断,判断数据库中是否已存在该人员信息,如果存在,不执行insert,返回人员信息即可。
  • 如果是在分布式环境下,服务部署一般都会采用Nginx来转发前端请求,如果Nginx采用轮询方式,第一次请求发送到A服务器,第二次服务发送到B服务器,两台服务器中获取synchronized锁都获取成功,在数据库中查询发现不存在人员信息,两台服务器都执行insert操作,最后数据库中一个人员信息生成了两台数据,出现问题。

出现上述情况,就可以用Redisson的分布式锁来解决。通过Redisson的单线程特性,在保存人员信息前先获取锁,获取成功后才执行。

二,Redisson分布式锁

1,引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.11.2</version>
</dependency>

2,生成RedissonClient对象

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        // 连接Redis,如果Redis设置了密码可以通过setPassword()指定密码
        config.useSingleServer().setAddress("redis://192.168.1.5:6379");
        return Redisson.create(config);
    }
}

3,测试

@GetMapping("/user")
public class UserController{
	@Autowired
	private UserService userService;
	@Autowired
	private RedissonClient redissonClient;
	
	@PostMapping
	public User save(User user){
		userService.save(user);
	}
}
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public User save(User user) {
        String key = "add_user_lock";
        RLock lock = null;
        try {
            lock = redissonClient.getLock(key+user.getPsnNo());
            while (true) {
                if (lock.tryLock()) {
                    break;
                }
                Thread.sleep(1000);
            }
            //业务代码
            String user=stringRedisTemplate.opsForValue().get(user.getPsnNo());
            if(user){
            	return (User)user;
            }else{
            	User user = userDao.save(user);//保存人员信息
	            stringRedisTemplate.opsForValue().set(user.getPsnNo(),user.toString());
	            return user;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != lock && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

三,说明

  • 通过Redis的单线程可以实现分布式锁,在第一次获得锁之后进行人员信息的保存,并将人员信息以人员编号,比如人员身份证号为key,存入到Redis中,第二次获得锁的请求,会先去Redis中查询是否存在人员信息,因为第一次请求已经将数据放入redis,第二次请求发现Redis中有数据,直接返回,防止插入多条数据。
  • Redisson除了上面的可重入锁外,还有其它的锁,可以根据实际情况进行选择。
//可重入锁
RLock lock = redissonClient.getLock("lock");
//公平锁
RLock lock = redissonClient.getFairLock("lock");
//信号量
RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
semaphore.trySetPermits(3);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值