当前位置:   article > 正文

基于Redisson 实现 Redis 分布式锁

基于Redisson 实现 Redis 分布式锁

代码示例:

  1. @GetMapping("/testJmeter")
  2. public void testJmeter() {
  3. synchronized (this){
  4. int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
  5. if (stock > 0) {
  6. int realStock = stock - 1;
  7. stringRedisTemplate.opsForValue().set("stock",realStock);
  8. System.out.println("扣减成功,剩余:"+ realStock);
  9. } else {
  10. System.out.println("扣减失败");
  11. }
  12. }
  13. }

问题:上面这种方法在单台Tomcat 服务中,可以实现 防止超卖问题,可是在实际项目开发中,我们部署项目一般不会只使用一台服务器 部署,一般都是多台服务器 通过 Nginx 实现服务代理,负载均衡。 这样简单使用 synchronized 是不可取的!!!

简单优化方法:

  1. @GetMapping("/testJmeter")
  2. public void testJmeter() {
  3. try {
  4. String lockKey = "lockKey";
  5. Boolean result = stringRedisTempalte.opsForValue.setIfAbsent(lockKey,"zhuzhe");
  6. if (!result) {
  7. return "错误";
  8. }
  9. int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
  10. if (stock > 0) {
  11. int realStock = stock - 1;
  12. stringRedisTemplate.opsForValue().set("stock",realStock);
  13. System.out.println("扣减成功,剩余:"+ realStock);
  14. } else {
  15. System.out.println("扣减失败");
  16. }
  17. } finaly{
  18. stringRedisTempalte.delete(lockKey);
  19. }
  20. }

问题:但是上述容易出现,某台机器突然宕机的现象,就无法走到finally 中去释放锁了。导师redis 中 的锁还没有释放。别的机器就无法获得锁了。 就会出现死锁问题。

优化方法:

可以实现对 给这把锁加上一个过期时间

  1. @GetMapping("/testJmeter")
  2. public void testJmeter() {
  3. try {
  4. String lockKey = "lockKey";
  5. Boolean result = stringRedisTempalte.opsForValue
  6. .setIfAbsent(lockKey,"zhuzhe",10,TimeUnit.SECONDS); // 设置过期时间
  7. if (!result) {
  8. return "错误";
  9. }
  10. int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
  11. if (stock > 0) {
  12. int realStock = stock - 1;
  13. stringRedisTemplate.opsForValue().set("stock",realStock);
  14. System.out.println("扣减成功,剩余:"+ realStock);
  15. } else {
  16. System.out.println("扣减失败");
  17. }} finaly{
  18. stringRedisTempalte.delete(lockKey);
  19. }
  20. }

 依然会有点问题--->失效时间不好设置,其中业务逻辑出现 SQL 慢查询导致代码执行时间长,在高并发情况下,会出现删 错锁的情况。A服务把 B服务中的锁 删除了。

优化方法:

每个线程都生成一个唯一id

  1. @GetMapping("/testJmeter")
  2. public void testJmeter() {
  3. try {
  4. String lockKey = "lockKey";
  5. String clientId = UUID.randomUUID.totring();
  6. Boolean result = stringRedisTempalte.opsForValue
  7. .setIfAbsent(lockKey ,clientId ,10,TimeUnit.SECONDS); // 设置过期时间
  8. if (!result) {
  9. return "错误";
  10. }
  11. int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
  12. if (stock > 0) {
  13. int realStock = stock - 1;
  14. stringRedisTemplate.opsForValue().set("stock",realStock);
  15. System.out.println("扣减成功,剩余:"+ realStock);
  16. } else {
  17. System.out.println("扣减失败");
  18. }} finaly{
  19. if (clientId.equals(stringRedisTempalte.opsForValue.getLockKey)){
  20. stringRedisTempalte.delete(lockKey);
  21. }
  22. }
  23. }

注意:其实还可以使用,创建一个定时任务,每10s 检查这个主线程的锁是否过期,如果快过期了,任务还没有结束,在延长30s.

最终解决方法:

引入 Redission 依赖

  1. <dependency>
  2. <groupId>org.redisson</groupId>
  3. <artifactId>redisson</artifactId>
  4. <version>3.6.5</version>
  5. </dependency>

配置:

  1. @Configuration
  2. public class RedissonConfig {
  3. @Value("${spring.redis.host}")
  4. private String address;
  5. @Value("${spring.redis.port}")
  6. private String port;
  7. @Value("${spring.redis.password}")
  8. private String password;
  9. @Bean
  10. public RedissonClient redissonClient() {
  11. Config config = new Config();
  12. //此处为单机模式 (注:有多种模式)
  13. config.useSingleServer()
  14. .setAddress("redis://" + address + ":"+port)
  15. .setPassword(password)
  16. .setTimeout(10000)
  17. .setRetryAttempts(5)
  18. .setRetryInterval(2000)
  19. .setConnectionPoolSize(64)
  20. .setConnectionMinimumIdleSize(24)
  21. // 保持连接活动
  22. .setKeepAlive(true)
  23. // 发送PING命令的间隔时间;
  24. .setPingConnectionInterval(30000);
  25. return Redisson.create(config);
  26. }
  27. }

 代码示例:

  1. @Rescourse
  2. private Redisson redisson;
  3. @GetMapping("/testJmeter")
  4. public void testJmeter() {
  5. try {
  6. String lockKey = "lockKey";
  7. String clientId = UUID.randomUUID.totring();
  8. //获取锁
  9. Rlock redissonLock = redisson.getLock(lockkey);
  10. //Boolean result = stringRedisTempalte.opsForValue
  11. .setIfAbsent(lockKey ,clientId ,10,TimeUnit.SECONDS); // 设置过期时间
  12. //if (!result) {
  13. // return "错误";
  14. // }
  15. //加锁
  16. redissonLock.lock()
  17. int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
  18. if (stock > 0) {
  19. int realStock = stock - 1;
  20. stringRedisTemplate.opsForValue().set("stock",realStock);
  21. System.out.println("扣减成功,剩余:"+ realStock);
  22. } else {
  23. System.out.println("扣减失败");
  24. }} finaly{
  25. //释放锁
  26. redissonLock.unlock()
  27. // if (clientId.equals(stringRedisTempalte.opsForValue.getLockKey)){
  28. //stringRedisTempalte.delete(lockKey);
  29. }
  30. }
  31. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/848386
推荐阅读
相关标签
  

闽ICP备14008679号