Java项目集成Redisson分布式锁

x33g5p2x  于2021-11-25 转载在 Java  
字(3.6k)|赞(0)|评价(0)|浏览(307)

一、为什么需要分布式锁?

因为Java中的锁,只作用于单个JVM实例上。而当下在互联网技术架构中,大家都用的分布式架构了,应用部署到多个服务器,这种情况下,线程之间的锁机制,就没作用了。为了解决这个问题,我们就引入分布式锁。

二、分布式锁要满足哪些要求呢?

(1)排他性:在同一时间只会有一个客户端能获取到锁,其它客户端无法同时获取。
(2)避免死锁:这把锁在一段有限的时间之后,一定会被释放。
(3)高可用:获取或释放锁的机制必须高可用且性能佳。

三、分布式锁的实现方式

这里只讲基于Redis实现的分布式锁,需要引入Redisson。

1、引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>${redisson.version}</version>
</dependency>

2、配置类

@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String address;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.database}")
    private Integer database;

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        address = StringUtils.strip(address.trim(), ",");
        String[] addressList = address.split(",");
        if (addressList.length == 1) {
            config.useSingleServer().setDatabase(database).setAddress("redis://" + addressList[0] + ":" + port);
            if (StringUtils.isNotEmpty(password)) {
                config.useSingleServer().setPassword(password);
            }
        } else {
            ClusterServersConfig clusterServersConfig = config.useClusterServers().setScanInterval(2000);
            for (String address : addressList) {
                clusterServersConfig.addNodeAddress("redis://" + address + ":" + port);
                if (StringUtils.isNotEmpty(password)) {
                    clusterServersConfig.setPassword(password);
                }
            }
        }

        return Redisson.create(config);
    }
}

3、配置文件

spring:
	redis:
	    host: 127.0.0.1
	    port: 6379
	    password: 
	    database: 0        # Redis数据库索引(默认为0)
	    lettuce:
	      pool:
	        max-active: 8   # 连接池最大连接数(使用负值表示没有限制) (默认为8)
	        max-wait: 0    # 连接池最大阻塞等待时间(使用负值表示没有限制) (默认为-1)
	        max-idle: 8    # 连接池中的最大空闲连接 (默认为8)
	        min-idle: 0    # 连接池中的最小空闲连接 (默认为0)

4、RedisOpService(接口)

public interface RedisOpService {
    Boolean isLocked(String lockName);

    Boolean lock(String lockName);

    Boolean unlock(String lockName);
}

5、RedisOpServiceImpl(实现类)

@Service
@Slf4j
public class RedisOpServiceImpl implements RedisOpService {
    @Resource
    private RedissonClient redissonClient;

    @Override
    public Boolean isLocked(String lockName) {
        RLock rLock = redissonClient.getLock(lockName);
        return rLock.isLocked();
    }

    @Override
    public Boolean lock(String lockName) {
        try {
            RLock rLock = redissonClient.getLock(lockName);
            // 锁一段时间后自动释放,防止死锁
            rLock.lock(10, TimeUnit.SECONDS);
            log.info("RedisLock lock [{}] success", lockName);
        } catch (Exception e) {
            log.error("RedisLock lock [{}] Exception:", lockName, e);
            return false;
        }
        return true;
    }

    @Override
    public Boolean unlock(String lockName) {
        try {
            RLock rLock = redissonClient.getLock(lockName);
            rLock.unlock();
            log.info("RedisLock unlock [{}] success", lockName);
        } catch (Exception e) {
            log.error("RedisLock unlock [{}] Exception:", lockName, e);
            return false;
        }
        return true;
    }
}

6、RedisController

@RestController
@Slf4j
public class RedisController {
    @Resource
    private RedisOpService redisOpService;

    @GetMapping("test")
    public void test() {
        // 分布式锁处理,情况1:有竞争时,后面的请求报错丢弃,及时响应消息“请稍后重试”
        String lockName = "test:" + 666;
        if (redisOpService.isLocked(lockName)) {
            throw new RuntimeException("请稍后重试");
        }
        try {
            if (redisOpService.lock(lockName)) {
                log.info("执行业务逻辑");
            }
        } catch (Exception e) {
            log.error("异常", e);
        } finally {
            redisOpService.unlock(lockName);
        }
    }
	
	@GetMapping("test2")
    public void test2() {
        // 分布式锁处理,情况2:有竞争时,阻塞等待,依次获取锁->执行->释放锁,直到都处理完成
        String lockName = "test:" + 777;
        try {
            if (redisOpService.lock(lockName)) {
                log.info("执行业务逻辑");
            }
        } catch (Exception e) {
            log.error("异常", e);
        } finally {
            redisOpService.unlock(lockName);
        }
    }
}

两种应用场景:

  • 情况1:有竞争时,后面的请求报错丢弃,及时响应消息“请稍后重试”;
  • 情况2:有竞争时,阻塞等待,依次获取锁->执行->释放锁,直到都处理完成。

相关文章