Redisson的使用

简介

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

原生的使用方式

在不使用redisson时,我们一般这样去使用Redis实现分布式锁。@Component

public class RedissonLockImpl implements RedissonLock {

private static ThreadLocal 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等已经实现了续命,保证我们的业务执行结束。

引入依赖

org.redisson

redisson

3.11.4

编写配置类@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 RBucket getRBucket(String objectName) {

RBucket bucket = redissonClient.getBucket(objectName);

return bucket;

}

/**

* 获取Map对象

*

* @param objectName

* @return

*/

public RMap getRMap(String objectName) {

RMap map = redissonClient.getMap(objectName);

return map;

}

/**

* 获取有序集合

*

* @param objectName

* @return

*/

public RSortedSet getRSortedSet(String objectName) {

RSortedSet sortedSet = redissonClient.getSortedSet(objectName);

return sortedSet;

}

/**

* 获取集合

*

* @param objectName

* @return

*/

public RSet getRSet(String objectName) {

RSet rSet = redissonClient.getSet(objectName);

return rSet;

}

/**

* 获取列表

*

* @param objectName

* @return

*/

public RList getRList(String objectName) {

RList rList = redissonClient.getList(objectName);

return rList;

}

/**

* 获取队列

*

* @param objectName

* @return

*/

public RQueue getRQueue(String objectName) {

RQueue rQueue = redissonClient.getQueue(objectName);

return rQueue;

}

/**

* 获取双端队列

*

* @param objectName

* @return

*/

public RDeque getRDeque(String objectName) {

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

}

}


————————————————
版权声明:本文为CSDN博主「灯灯藕粉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_36027833/article/details/113681484

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐