Redisson 实现分布式锁
Redisson 是一个 Java 操作 Redis 的客户端,提供了大量的分布式数据集来简化对 Redis 的操作和使用,可以让开发者像使用本地集合一样使用 Redis(继承了和 Java 相同的集合类)
关键词:Java Redis 客户端,分布式数据结构,实现了很多 Java 里支持的集合
两种引用方式
- spring boot starter 引入(不推荐,因为版本迭代太快,容易冲突):
https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter
- 直接引入:https://github.com/redisson/redisson#quick-start
(1)引入依赖
1 2 3 4 5
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.17.5</version> </dependency>
|
(2)新建 RedissonConfig 配置类,配置地址、端口、创建实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import lombok.Data; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @Data @ConfigurationProperties(prefix = "spring.redis") public class RedissonConfig {
private String host; private String port; @Bean public RedissonClient redissonClient(){ Config config = new Config(); String redisAddress = String.format("redis://%s:%s", host, port); config.useSingleServer().setAddress(redisAddress).setDatabase(3); RedissonClient redisson = Redisson.create(config); return redisson; } }
|
使用Redisson
创建分布式列表、Map,新建测试类示例代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Resource private RedissonClient redissonClient; @Test void test(){ List<String> list = new ArrayList<>(); list.add("yupi"); System.out.println("list:" + list.get(0));
RList<Object> rList = redissonClient.getList("test-list"); rList.add("yupi"); System.out.println("RList:" + rList.get(0));
RMap<Object, Object> map = redissonClient.getMap("test-map"); map.put("yupi", 10); map.get("yupi");
System.out.println(map.get("yupi")); }
|
分布式锁保证定时任务不重复执行
实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void testWatchDog(){ RLock lock = redissonClient.getLock("yupao:precachejob:docache:lock"); try { if(lock.tryLock(0, -1, TimeUnit.MILLISECONDS)){ dosomething(); System.out.println("getLock:" + Thread.currentThread().getId()); } } catch (InterruptedException e) { System.out.println(e.getMessage()); } finally { if(lock.isHeldByCurrentThread()){ System.out.println("unLock:" + Thread.currentThread().getId()); lock.unlock(); } }
}
|
注意:
- waitTime 设置为 0,只抢一次,抢不到就放弃
- 主要释放锁要写在 finally 语句块中,保证最后一定释放锁
Redisson 看门狗机制
Redisson 中提供的续期机制
开一个监听线程,如果方法还没执行完,就帮你重置 Redis 锁的过期时间
原理:
- 监听当前线程,默认过期时间是 30 秒,每 10 秒续期一次(续期到 30 秒)
- 如果线程挂掉(注意 debug 模式也会被它当成服务器宕机),则不会续期
详情参考文档:https://blog.csdn.net/qq_26222859/article/details/79645203
定时任务全部代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yupi.yupao.model.domain.User; import com.yupi.yupao.service.UserService; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit;
@Component @Slf4j public class PreCacheJob {
@Resource private UserService userService; @Resource private RedisTemplate<String, Object> redisTemplate;
@Resource private RedissonClient redissonClient;
private List<Long> mainUserList = Arrays.asList(1L);
@Scheduled(cron = "0 37 1 * * *") public void doCacheRecommendUser(){ RLock lock = redissonClient.getLock("yupao:precachejob:docache:lock"); try { if(lock.tryLock(0, -1, TimeUnit.MILLISECONDS)){ System.out.println("getLock:" + Thread.currentThread().getId()); for (Long userId : mainUserList) { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); Page<User> userPage = userService.page(new Page<>(1, 20), queryWrapper); String redisKey = String.format("yupao:user:recommend:%s", userId); ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); try { valueOperations.set(redisKey, userPage, 30000, TimeUnit.MILLISECONDS); } catch (Exception e) { log.error("redis set key error", e); } } } } catch (InterruptedException e) { log.error("doCacheRecommendUser error", e); } finally { if(lock.isHeldByCurrentThread()){ System.out.println("unLock:" + Thread.currentThread().getId()); lock.unlock(); } }
} }
|