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%;" />
参考
参考