Redisson的应用

简介

在当初的我的项目中,常常会有并发问题,解决并发问题的一个思路就是应用分布式锁。在以前的我的项目中,常常会应用Redis的setnx个性来实现分布式锁,然而有可能会带来死锁的问题,那么就能够应用Redisson来实现Redis的分布式锁。这里我应用的场景是短信验证码服务,同一时刻只能有一个线程给同一个手机号发送短信。

原生的应用形式

在不应用redisson时,咱们个别这样去应用Redis实现分布式锁。

@Componentpublic class RedissonLockImpl implements RedissonLock {    private static ThreadLocal<String>  threadLocal = new ThreadLocal<>();    @Autowired    private StringRedisTemplate redisTemplate;    @Override    public boolean tryLock(String key, long timeout, TimeUnit timeUnit) {        if (threadLocal.get() == null){            String lockName = "redis:test:name";            threadLocal.set(lockName);            return redisTemplate.opsForValue().setIfAbsent(key, lockName, timeout, timeUnit);        }else if (threadLocal.get().equals(redisTemplate.opsForValue().get(key))){            return true;        }        return false;    }}

而后在代码中这样应用:

@RestControllerpublic class RedissonController {    @Autowired    private StringRedisTemplate redisTemplate;    @Autowired    private RedissonLock redissonLock;    @RequestMapping("/submitOrder")    public String submitOrder(){        String key = "test";        // 这里可能存在在你设置的工夫内以后线程还没有完结        boolean lock = redissonLock.tryLock(key, 4, TimeUnit.SECONDS);        if (!lock){            return "error";        }        try {            // 具体的业务逻辑            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));            if (stock>0){                // 下单                stock = stock - 1;                redisTemplate.opsForValue().set("stock", stock+"");                return "success";            } else {                System.out.println("库存有余");                return "false";            }        } finally {            // 这里能够开释锁        }    }}

这样可能就会导致在你设置的锁的工夫完结后,该线程还没有执行完结,然而锁曾经开释,就会导致前面获取锁,开释锁的程序变乱,导致业务出问题。

原生改良

对于这种某个线程超时的问题,能够给这个线程new一个守护线程,守护线程每10s去刷新这个工夫实现续命。主线程运行完结,守护线程就会本人死亡,不须要咱们操作。(然而个别线程都是线程池,线程不会死亡,守护线程也就不能主动死亡,所以有危险)。

当然你能够手动实现一个线程专门保护这个续命的工夫,然而实现起来就会很麻烦。所以咱们举荐间接应用redisson。redisson自身曾经实现了这里的续命。

Redisson的具体应用

Redisson中的Rlock等曾经实现了续命,保障咱们的业务执行完结。

引入依赖

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

编写配置类

@Configurationpublic class RedissonConfig {    @Value("${spring.redis.host}")    private String host;    @Value("${spring.redis.port}")    private String port;    @Value("${spring.redis.password}")    private String password;    @Bean    public RedissonClient redissonClient(){        Config config = new Config();        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);        //增加主从配置//        config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});        return Redisson.create(config);    }}

在代码中应用

这样咱们能够主动注入RedissonClient进行应用。然而个别咱们能够在下面再包装一层service,来包装咱们个别的异样等场景。

/** * redisson操作类 * @author 朱友德 */@Service("redissonService")public class RedissonService {    @Autowired    private RedissonClient redissonClient;    public void getRedissonClient() throws IOException {        Config config = redissonClient.getConfig();        System.out.println(config.toJSON().toString());    }    /**`     * 获取字符串对象     *     * @param objectName     * @return     */    public <T> RBucket<T> getRBucket(String objectName) {        RBucket<T> bucket = redissonClient.getBucket(objectName);        return bucket;    }    /**     * 获取Map对象     *     * @param objectName     * @return     */    public <K, V> RMap<K, V> getRMap(String objectName) {        RMap<K, V> map = redissonClient.getMap(objectName);        return map;    }    /**     * 获取有序汇合     *     * @param objectName     * @return     */    public <V> RSortedSet<V> getRSortedSet(String objectName) {        RSortedSet<V> sortedSet = redissonClient.getSortedSet(objectName);        return sortedSet;    }    /**     * 获取汇合     *     * @param objectName     * @return     */    public <V> RSet<V> getRSet(String objectName) {        RSet<V> rSet = redissonClient.getSet(objectName);        return rSet;    }    /**     * 获取列表     *     * @param objectName     * @return     */    public <V> RList<V> getRList(String objectName) {        RList<V> rList = redissonClient.getList(objectName);        return rList;    }    /**     * 获取队列     *     * @param objectName     * @return     */    public <V> RQueue<V> getRQueue(String objectName) {        RQueue<V> rQueue = redissonClient.getQueue(objectName);        return rQueue;    }    /**     * 获取双端队列     *     * @param objectName     * @return     */    public <V> RDeque<V> getRDeque(String objectName) {        RDeque<V> rDeque = redissonClient.getDeque(objectName);        return rDeque;    }    /**     * 获取锁     * @param objectName     * @return     */    public RLock getRLock(String objectName) {        RLock rLock = redissonClient.getLock(objectName);        return rLock;    }    public Boolean tryLock(String key, long leaseTime, TimeUnit unit) {        RLock rLock = redissonClient.getLock(key);        boolean tryLock = false;        try {            tryLock = rLock.tryLock(0, leaseTime, unit);        } catch (InterruptedException e) {            return false;        }        return tryLock;    }    public Boolean verifyTryLock(RLock rLock, long leaseTime, TimeUnit unit) {        boolean tryLock = false;        try {            tryLock = rLock.tryLock(0, leaseTime, unit);        } catch (InterruptedException e) {            return false;        }        return tryLock;    }    /**     * 获取读取锁     *     * @param objectName     * @return     */    public RReadWriteLock getRWLock(String objectName) {        RReadWriteLock rwlock = redissonClient.getReadWriteLock(objectName);        return rwlock;    }    /**     * 获取原子数     *     * @param objectName     * @return     */    public RAtomicLong getRAtomicLong(String objectName) {        RAtomicLong rAtomicLong = redissonClient.getAtomicLong(objectName);        return rAtomicLong;    }    /**     * 获取记数锁     *     * @param objectName     * @return     */    public RCountDownLatch getRCountDownLatch(String objectName) {        RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch(objectName);        return rCountDownLatch;    }

具体业务场景应用

@Servicepublic class MyService {    @Autowired    private RedissonService redissonService;    @Autowired    private StringRedisTemplate redisTemplate;    public String sendCodeByTemplate(final String phone){               // 过期工夫        String ttlKey = "redis:ttl:phone:"+phone;        String key = "redis:phone:"+phone;        // 对这个手机号发送音讯时会对发送手机号加锁        if (!redissonService.tryLock(key, 10, TimeUnit.SECONDS)){            //获取过期工夫            Long ttl = redisTemplate.getExpire(ttlKey);            //发送验证码频繁            return "发送频繁";        }                // 发送验证码        sendCode(phone);        return "true";    }        private boolean sendCode(String phone){        // 写具体发送逻辑        return true;    }}

<img src="https://gitee.com/jsnucrh/blog-sharding_1/raw/master/img/20201213230304.png" alt="image-20201213230304098" style="zoom:150%;" />

参考

参考