spring 集成Redisson,实现分布式锁

向右看齐 2023-02-13 05:45 95阅读 0赞

使用的业务场景:

例如:电商项目的分布式系统,需要定时关闭超时的订单信息,我们会利用spring的调度定时去处理,但由于是分布式的会互相竞争资源,所以需要用到分布式锁,让每个进程之间互相去竞争锁,获得锁的进程才去执行任务。这样就能保证事务的原子性,代码的健壮性。

0、pom.xml

  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>2.6.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>redis.clients</groupId>
  8. <artifactId>jedis</artifactId>
  9. <version>2.6.0</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.projectlombok</groupId>
  13. <artifactId>lombok</artifactId>
  14. <version>1.16.18</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.redisson</groupId>
  18. <artifactId>redisson</artifactId>
  19. <version>2.9.0</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>com.fasterxml.jackson.dataformat</groupId>
  23. <artifactId>jackson-dataformat-avro</artifactId>
  24. <version>2.9.0</version>
  25. </dependency>

1、redis.properties

  1. #redis config start
  2. redis1.ip=127.0.0.1
  3. redis1.port=6379
  4. redis2.ip=127.0.0.1
  5. redis2.port=6380
  6. #最大连接数
  7. redis.max.total=20
  8. #最大空闲数
  9. redis.max.idle=10
  10. #最小空闲数
  11. redis.min.idle=2
  12. #从jedis连接池获取连接时,校验并返回可用的连接
  13. redis.test.borrow=true
  14. #把连接放回jedis连接池时,校验并返回可用的连接
  15. redis.test.return=false
  16. #redis config end
  17. #closeOrderTaskTime start
  18. close.order.task.time.hour=2
  19. #毫秒数
  20. lock.timeout=5000
  21. #closeOrderTaskTime end

2、RedissonManager(redisson配置类)

  1. package com.jack.common;
  2. import com.jack.util.PropertiesUtil;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.redisson.Redisson;
  5. import org.redisson.config.Config;
  6. import org.springframework.stereotype.Component;
  7. import javax.annotation.PostConstruct;
  8. /** * Created by jack */
  9. @Component
  10. @Slf4j
  11. public class RedissonManager {
  12. private Config config = new Config();
  13. private Redisson redisson = null;
  14. public Redisson getRedisson() {
  15. return redisson;
  16. }
  17. private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
  18. private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
  19. private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
  20. private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));
  21. @PostConstruct
  22. private void init(){
  23. try {
  24. config.useSingleServer().setAddress(new StringBuilder().append(redis1Ip).append(":").append(redis1Port).toString());
  25. redisson = (Redisson) Redisson.create(config);
  26. log.info("初始化Redisson结束");
  27. } catch (Exception e) {
  28. log.error("redisson init error",e);
  29. }
  30. }
  31. }

3、CloseOrderTask(关闭订单的定时任务类)

其中closeOrderTaskV4是利用了redisson的分布式锁。
closeOrderTaskV3是用的原生手动方法。
closeOrderTaskV2和closeOrderTaskV1是有缺陷的方法。

  1. package com.jack.task;
  2. import com.jack.common.Const;
  3. import com.jack.common.RedissonManager;
  4. import com.jack.service.IOrderService;
  5. import com.jack.util.PropertiesUtil;
  6. import com.jack.util.RedisShardedPoolUtil;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.apache.commons.lang.StringUtils;
  9. import org.redisson.api.RLock;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.scheduling.annotation.Scheduled;
  12. import org.springframework.stereotype.Component;
  13. import javax.annotation.PreDestroy;
  14. import java.util.concurrent.TimeUnit;
  15. /** * Created by jack */
  16. @Component
  17. @Slf4j
  18. public class CloseOrderTask {
  19. @Autowired
  20. private IOrderService iOrderService;
  21. @Autowired
  22. private RedissonManager redissonManager;
  23. @PreDestroy
  24. public void delLock(){
  25. RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  26. }
  27. // @Scheduled(cron="0 */1 * * * ?")//每1分钟(每个1分钟的整数倍)
  28. public void closeOrderTaskV1(){
  29. log.info("关闭订单定时任务启动");
  30. int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
  31. // iOrderService.closeOrder(hour);
  32. log.info("关闭订单定时任务结束");
  33. }
  34. // @Scheduled(cron="0 */1 * * * ?")
  35. public void closeOrderTaskV2(){
  36. log.info("关闭订单定时任务启动");
  37. long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
  38. Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
  39. if(setnxResult != null && setnxResult.intValue() == 1){
  40. //如果返回值是1,代表设置成功,获取锁
  41. closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  42. }else{
  43. log.info("没有获得分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  44. }
  45. log.info("关闭订单定时任务结束");
  46. }
  47. //@Scheduled(cron="0 */1 * * * ?")
  48. public void closeOrderTaskV3(){
  49. log.info("关闭订单定时任务启动");
  50. long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
  51. Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
  52. if(setnxResult != null && setnxResult.intValue() == 1){
  53. closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  54. }else{
  55. //未获取到锁,继续判断,判断时间戳,看是否可以重置并获取到锁
  56. String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  57. if(lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)){
  58. String getSetResult = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
  59. //再次用当前时间戳getset。
  60. //返回给定的key的旧值,->旧值判断,是否可以获取锁
  61. //当key没有旧值时,即key不存在时,返回nil ->获取锁
  62. //这里我们set了一个新的value值,获取旧的值。
  63. if(getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr,getSetResult))){
  64. //真正获取到锁
  65. closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  66. }else{
  67. log.info("没有获取到分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  68. }
  69. }else{
  70. log.info("没有获取到分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  71. }
  72. }
  73. log.info("关闭订单定时任务结束");
  74. }
  75. @Scheduled(cron="0 */1 * * * ?")
  76. public void closeOrderTaskV4(){
  77. RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  78. boolean getLock = false;
  79. try {
  80. if(getLock = lock.tryLock(0,50, TimeUnit.SECONDS)){
  81. log.info("Redisson获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
  82. int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
  83. // iOrderService.closeOrder(hour);
  84. }else{
  85. log.info("Redisson没有获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
  86. }
  87. } catch (InterruptedException e) {
  88. log.error("Redisson分布式锁获取异常",e);
  89. } finally {
  90. if(!getLock){
  91. return;
  92. }
  93. lock.unlock();
  94. log.info("Redisson分布式锁释放锁");
  95. }
  96. }
  97. private void closeOrder(String lockName){
  98. RedisShardedPoolUtil.expire(lockName,5);//有效期50秒,防止死锁
  99. log.info("获取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
  100. int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
  101. iOrderService.closeOrder(hour);
  102. RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
  103. log.info("释放{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
  104. log.info("===============================");
  105. }
  106. }

发表评论

表情:
评论列表 (有 0 条评论,95人围观)

还没有评论,来说两句吧...

相关阅读