乐趣区

关于程序员:Redisson的使用

Redisson 的应用

简介

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

原生的应用形式

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

@Component
public 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;
    }
}

而后在代码中这样应用:

@RestController
public 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>

编写配置类

@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;

    @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;
    }

具体业务场景应用

@Service
public 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%;” />

参考

参考

退出移动版