热爱技术,追求卓越
不断求索,精益求精

springboot使用redisson作为分布式锁的一种实现方式

redisson是一个不错的开源作品,在最近的项目中主要用到它的分布式锁,今天就来谈谈springboot是如何使用redisson作为分布式锁的。

引入重要的两个依赖,一个是spring-boot-starter-data-redis,一个是redisson:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.7.5</version>
</dependency>

application.properties中的相关配置:

# Redis服务器地址(默认session使用)
spring.redis.host=172.17.0.2
# Redis服务器连接密码(默认为空)
spring.redis.password=lovecto
# Redis服务器连接端口
spring.redis.port=6390

接口编程的思想还是要保持的。我们定义一个Loker接口,用于分布式锁的一些操作:

package cn.lovecto.promotion.lock;

import java.util.concurrent.TimeUnit;

/**
 * 锁接口
 * 
 *
 */
public interface Locker {

    /**
     * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
     * 
     * @param lockKey
     */
    void lock(String lockKey);

    /**
     * 释放锁
     * 
     * @param lockKey
     */
    void unlock(String lockKey);

    /**
     * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
     * 
     * @param lockKey
     * @param timeout
     */
    void lock(String lockKey, int timeout);

    /**
     * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
     * 
     * @param lockKey
     * @param unit
     * @param timeout
     */
    void lock(String lockKey, TimeUnit unit, int timeout);

    /**
     * 尝试获取锁,获取到立即返回true,未获取到立即返回false
     * 
     * @param lockKey
     * @return
     */
    boolean tryLock(String lockKey);

    /**
     * 尝试获取锁,在等待时间内获取到锁则返回true,否则返回false,如果获取到锁,则要么执行完后程序释放锁,
     * 要么在给定的超时时间leaseTime后释放锁
     * 
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @param unit
     * @return
     */
    boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
            throws InterruptedException;

    /**
     * 锁是否被任意一个线程锁持有
     * 
     * @param lockKey
     * @return
     */
    boolean isLocked(String lockKey);

}

有了Locker接口,我们再添加一个基于Redisson的实现类RedissonLocker,实现Locker中的方法:

package cn.lovecto.promotion.lock;

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

/**
 * 基于Redisson的分布式锁
 *
 */
public class RedissonLocker implements Locker {

    private RedissonClient redissonClient;

    public RedissonLocker(RedissonClient redissonClient) {
        super();
        this.redissonClient = redissonClient;
    }

    @Override
    public void lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    @Override
    public void lock(String lockKey, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
    }

    @Override
    public void lock(String lockKey, TimeUnit unit, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
    }

    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(String lockKey, long waitTime, long leaseTime,
            TimeUnit unit) throws InterruptedException{
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock(waitTime, leaseTime, unit);
    }

    @Override
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isLocked();
    }

}

有了Locker和实现类RedissonLocker,我们总不能一直去创建RedissonLocker对象或者不断的在每个要使用到分布式锁的地方都注入RedissonLocker的对象,所以我们定义一个工具类LockUtil,到时候想哪里使用就直接使用工具类的静态方法就行了:

package cn.lovecto.promotion.utils;

import java.util.concurrent.TimeUnit;

import cn.lovecto.promotion.lock.Locker;

/**
 * redis分布式锁工具类
 *
 */
public final class LockUtil {

    private static Locker locker;

    /**
     * 设置工具类使用的locker
     * @param locker
     */
    public static void setLocker(Locker locker) {
        LockUtil.locker = locker;
    }

    /**
     * 获取锁
     * @param lockKey
     */
    public static void lock(String lockKey) {
        locker.lock(lockKey);
    }

    /**
     * 释放锁
     * @param lockKey
     */
    public static void unlock(String lockKey) {
        locker.unlock(lockKey);
    }

    /**
     * 获取锁,超时释放
     * @param lockKey
     * @param timeout
     */
    public static void lock(String lockKey, int timeout) {
        locker.lock(lockKey, timeout);
    }

    /**
     * 获取锁,超时释放,指定时间单位
     * @param lockKey
     * @param unit
     * @param timeout
     */
    public static void lock(String lockKey, TimeUnit unit, int timeout) {
        locker.lock(lockKey, unit, timeout);
    }

    /**
     * 尝试获取锁,获取到立即返回true,获取失败立即返回false
     * @param lockKey
     * @return
     */
    public static boolean tryLock(String lockKey) {
        return locker.tryLock(lockKey);
    }

    /**
     * 尝试获取锁,在给定的waitTime时间内尝试,获取到返回true,获取失败返回false,获取到后再给定的leaseTime时间超时释放
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @param unit
     * @return
     * @throws InterruptedException
     */
    public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
            TimeUnit unit) throws InterruptedException {
        return locker.tryLock(lockKey, waitTime, leaseTime, unit);
    }

    /**
     * 锁释放被任意一个线程持有
     * @param lockKey
     * @return
     */
    public static boolean isLocked(String lockKey) {
        return locker.isLocked(lockKey);
    }
}

“油盐酱醋米”有了,现在我们开始配置吧,创建一个redisson的配置类RedissonConfig,内容如下:

package cn.lovecto.promotion.config;

import java.io.IOException;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.lovecto.promotion.lock.RedissonLocker;
import cn.lovecto.promotion.utils.LockUtil;

@Configuration
public class RedissonConfig {

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

    /**
     * RedissonClient,单机模式
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() throws IOException {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        return Redisson.create(config);
    }

    @Bean
    public RedissonLocker redissonLocker(RedissonClient redissonClient){
        RedissonLocker locker = new RedissonLocker(redissonClient);
        //设置LockUtil的锁处理对象
        LockUtil.setLocker(locker);
        return locker;
    }
}

现在就可以开始使用我们的分布式锁了,使用方式如下:

//加锁
LockUtil.lock(KEY);
try {
    //TODO 干事情
} catch (Exception e) {
    //异常处理
}finally{
    //释放锁
    LockUtil.unlock(KEY);
}

是不是看到try catch finally比较不爽,没关系,你可以使用AOP呀,这样在需要使用分布式锁的方法上加入注解就可以解决,可以参考AOP相关的文章如:《使用ThreadLocal和AOP做线程缓存提高性能,缩短API网关响应时间》、《springboot项目使用自定义注解和aop记录类名方法名参数耗时信息》、《秒懂java,MyBatis分页插件PageHelper基于ThreadLocal的实现原理分析》等。思想都一样的,用了AOP后,你可以在执行方法的前面加锁,方法执行结束后释放锁,注解的参数可以控制AOP的行为。

赞(9)
未经允许不得转载:LoveCTO » springboot使用redisson作为分布式锁的一种实现方式

热爱技术 追求卓越 精益求精