使用StringRedisTemplate实现redis分布式锁
背景:单个接口可能同时被多个用户调用,但是每个用户使用的数据都是不一样,因此需要使用分布式锁解决数据减少了没有即使减少的问题
使用的指令来自的edis的setnx命令,setnx(k,v1),setnx(k.v2),当设置值为v1后,v2的设置无效
上图中启动了两台一样的服务,大多数情况,同一时间只有一条日志打印,但是定时任务是每15秒执行一次,数据有效期15秒,所以会有同一秒不同毫秒的日志打印
当时间设置为
timed.task=0 30,35,40 17 * * *
同一秒内无法保证只执行一次
但是到了毫秒级别,不一样,实际上是不一样用户,但是定时任务部署多台服务器,这样可能存在问题
上图看到没有了在一秒内重合的日志打印,这边是调整了锁的过期时间
该行代码保证了原子性 Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "001", 25, TimeUnit.SECONDS);
相关代码部分
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.redis</groupId> <artifactId>redis-service</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> </parent> <dependencies> <!--tomcat容器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--lombok依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.1</version> </dependency> </dependencies> </project>
package com.redis; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; /** * @Description: * @Author: Yourheart * @Create: 2023/2/28 14:38 */ @SpringBootApplication @EnableAsync public class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } }
package com.redis.task; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * @Description: * @Author: Yourheart * @Create: 2023/2/28 14:51 */ @Configuration //1.主要用于标记配置类,兼备Component的效果。 @EnableScheduling // 2.开启定时任务 @Slf4j public class RedisTimeTask { @Autowired private StringRedisTemplate stringRedisTemplate; @Scheduled(cron = "${timed.task}")//每天上午9点,下午5点执行 /** * 单位都是毫秒 */ // @Scheduled(initialDelay = 5000,fixedRate=5000) // @Scheduled(initialDelay = 5000,fixedRate=20000) @Async public void timerSendMessage(){ String lockKey="test_001"; // Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "001"); // stringRedisTemplate.expire(lockKey,25, TimeUnit.SECONDS); Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "001", 25, TimeUnit.SECONDS); if (setIfAbsent){ try { log.info("测试redis的分布式锁成功...."); } finally { stringRedisTemplate.delete(lockKey); } } } }
server.port=8081 # 当天17点25分0秒 #timed.task=0 05,10,15,20,25,30,35,40,45,50,55 * * * * #每隔15秒执行一次 timed.task=0/15 * * * * * #timed.task= 0 0/5 * * * * #redis配置 spring.redis.host=127.0.0.1 spring.redis.port=6380 spring.redis.database=1 spring.redis.jedis.pool.max-active=8 spring.redis.jedis.pool.min-idle=0 spring.redis.password= spring.redis.lettuce.pool.max-wait=1000 logging.level.com.redis=debug logging.level.web=debug spring.devtools.add-properties=false