使用redis和shedlock怎么实现分布式锁
发表于:2025-12-03 作者:千家信息网编辑
千家信息网最后更新 2025年12月03日,使用redis和shedlock怎么实现分布式锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1. jar包的引入
千家信息网最后更新 2025年12月03日使用redis和shedlock怎么实现分布式锁
使用redis和shedlock怎么实现分布式锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
1. jar包的引入
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-starter-data-redis
net.javacrumbs.shedlock
shedlock-provider-redis-spring
2.3.0
org.apache.commons
commons-pool2
2.0
net.javacrumbs.shedlock
shedlock-spring
2.3.0
org.projectlombok
lombok
com.github.xiaoymin
swagger-bootstrap-ui
1.9.6
io.springfox
springfox-swagger2
2.9.2
org.aspectj
aspectjweaver
1.9.2
2. redis的配置
配置文件
#redis
redis.host=192.168.1.6
redis.password=
redis.port=6379
redis.taskScheduler.poolSize=100
redis.taskScheduler.defaultLockMaxDurationMinutes=10
redis.default.timeout=10
redisCache.expireTimeInMilliseconds=1200000
配置类
package com.example.redis_demo_limit.redis;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;
import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration;
import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Value("${redis.host}")
private String redisHost;
@Value("${redis.port}")
private int redisPort;
@Value("${redis.password}")
private String password;
@Value("${redis.taskScheduler.poolSize}")
private int tasksPoolSize;
@Value("${redis.taskScheduler.defaultLockMaxDurationMinutes}")
private int lockMaxDuration;
@Bean(destroyMethod = "shutdown")
ClientResources clientResources() {
return DefaultClientResources.create();
}
@Bean
public RedisStandaloneConfiguration redisStandaloneConfiguration() {
RedisStandaloneConfiguration redisStandaloneConfiguration =
new RedisStandaloneConfiguration(redisHost, redisPort);
if (password != null && !password.trim().equals("")) {
RedisPassword redisPassword = RedisPassword.of(password);
redisStandaloneConfiguration.setPassword(redisPassword);
}
return redisStandaloneConfiguration;
}
@Bean
public ClientOptions clientOptions() {
return ClientOptions.builder()
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
.autoReconnect(true).build();
}
@Bean
LettucePoolingClientConfiguration lettucePoolConfig(ClientOptions options, ClientResources dcr) {
return LettucePoolingClientConfiguration.builder().poolConfig(new GenericObjectPoolConfig())
.clientOptions(options).clientResources(dcr).build();
}
@Bean
public RedisConnectionFactory connectionFactory(
RedisStandaloneConfiguration redisStandaloneConfiguration,
LettucePoolingClientConfiguration lettucePoolConfig) {
return new LettuceConnectionFactory(redisStandaloneConfiguration, lettucePoolConfig);
}
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@Primary
public RedisTemplate操作类
package com.example.redis_demo_limit.redis;
public interface DataCacheRepository {
boolean add(String collection, String hkey, T object, Long timeout);
boolean delete(String collection, String hkey);
T find(String collection, String hkey, Class tClass);
Boolean isAvailable();
/**
* redis 加锁
*
* @param key
* @param second
* @return
*/
Boolean lock(String key, String value, Long second);
Object getValue(String key);
/**
* redis 解锁
*
* @param key
* @return
*/
void unLock(String key);
void setIfAbsent(String key, long value, long ttl);
void increment(String key);
Long get(String key);
void set(String key, long value, long ttl);
void set(Object key, Object value, long ttl);
Object getByKey(String key);
void getLock(String key, String clientID) throws Exception;
void releaseLock(String key, String clientID);
boolean hasKey(String key);
}
实现类
package com.example.redis_demo_limit.redis;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Repository;
import java.time.Duration;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@Slf4j
@Repository
public class CacheRepository implements com.example.redis_demo_limit.redis.DataCacheRepository {
private static final ObjectMapper OBJECT_MAPPER;
private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC");
static {
OBJECT_MAPPER = new ObjectMapper();
OBJECT_MAPPER.setTimeZone(DEFAULT_TIMEZONE);
}
Logger logger = LoggerFactory.getLogger(CacheRepository.class);
@Autowired
RedisTemplate template; // and we're in business
@Value("${redis.default.timeout}00")
Long defaultTimeOut;
public boolean addPermentValue(String collection, String hkey, T object) {
try {
String jsonObject = OBJECT_MAPPER.writeValueAsString(object);
template.opsForHash().put(collection, hkey, jsonObject);
return true;
} catch (Exception e) {
logger.error("Unable to add object of key {} to cache collection '{}': {}", hkey, collection,
e.getMessage());
return false;
}
}
@Override
public boolean add(String collection, String hkey, T object, Long timeout) {
Long localTimeout;
if (timeout == null) {
localTimeout = defaultTimeOut;
} else {
localTimeout = timeout;
}
try {
String jsonObject = OBJECT_MAPPER.writeValueAsString(object);
template.opsForHash().put(collection, hkey, jsonObject);
template.expire(collection, localTimeout, TimeUnit.SECONDS);
return true;
} catch (Exception e) {
logger.error("Unable to add object of key {} to cache collection '{}': {}", hkey, collection,
e.getMessage());
return false;
}
}
@Override
public boolean delete(String collection, String hkey) {
try {
template.opsForHash().delete(collection, hkey);
return true;
} catch (Exception e) {
logger.error("Unable to delete entry {} from cache collection '{}': {}", hkey, collection,
e.getMessage());
return false;
}
}
@Override
public T find(String collection, String hkey, Class tClass) {
try {
String jsonObj = String.valueOf(template.opsForHash().get(collection, hkey));
return OBJECT_MAPPER.readValue(jsonObj, tClass);
} catch (Exception e) {
if (e.getMessage() == null) {
logger.error("Entry '{}' does not exist in cache", hkey);
} else {
logger.error("Unable to find entry '{}' in cache collection '{}': {}", hkey, collection,
e.getMessage());
}
return null;
}
}
@Override
public Boolean isAvailable() {
try {
return template.getConnectionFactory().getConnection().ping() != null;
} catch (Exception e) {
logger.warn("Redis server is not available at the moment.");
}
return false;
}
@Override
public Boolean lock(String key, String value, Long second) {
Boolean absent = template.opsForValue().setIfAbsent(key, value, second, TimeUnit.SECONDS);
return absent;
}
@Override
public Object getValue(String key) {
return template.opsForValue().get(key);
}
@Override
public void unLock(String key) {
template.delete(key);
}
@Override
public void increment(String key) {
RedisAtomicLong counter = new RedisAtomicLong(key, template.getConnectionFactory());
counter.incrementAndGet();
}
@Override
public void setIfAbsent(String key, long value, long ttl) {
ValueOperations ops = template.opsForValue();
ops.setIfAbsent(key, value, Duration.ofSeconds(ttl));
}
@Override
public Long get(String key) {
RedisAtomicLong counter = new RedisAtomicLong(key, template.getConnectionFactory());
return counter.get();
}
@Override
public void set(String key, long value, long ttl) {
RedisAtomicLong counter = new RedisAtomicLong(key, template.getConnectionFactory());
counter.set(value);
counter.expire(ttl, TimeUnit.SECONDS);
}
@Override
public void set(Object key, Object value, long ttl) {
template.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
}
@Override
public Object getByKey(String key) {
return template.opsForValue().get(key);
}
@Override
public void getLock(String key, String clientID) throws Exception {
Boolean lock = false;
// 重试3次,每间隔1秒重试1次
for (int j = 0; j <= 3; j++) {
lock = lock(key, clientID, 10L);
if (lock) {
log.info("获得锁》》》" + key);
break;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.error("线程休眠异常", e);
break;
}
}
// 重试3次依然没有获取到锁,那么返回服务器繁忙,请稍后重试
if (!lock) {
throw new Exception("服务繁忙");
}
}
@Override
public void releaseLock(String key, String clientID) {
if (clientID.equals(getByKey(key))) {
unLock(key);
}
}
@Override
public boolean hasKey(String key) {
return template.hasKey(key);
}
}
三、使用方法
import com.example.redis_demo_limit.annotation.LimitedAccess;
import com.example.redis_demo_limit.redis.DataCacheRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/redis")
public class RedisController {
private static final String KEY = "key";
@Resource
private DataCacheRepository dataCacheRepository;
@LimitedAccess(frequency = 1,second = 1)
@PostMapping("/add")
public String add(String str){
dataCacheRepository.set("str","add success",200L);
return "success";
}
//分布式锁使用示例
@PostMapping("/pay")
public String pay(String userName,Integer account){
String clientID = UUID.randomUUID().toString();
//设置锁的过期时间,避免死锁
Boolean lock = dataCacheRepository.lock(userName, clientID, 6000L);
if(!lock){
log.info("未获取到锁{}", userName);
return "程序繁忙,请稍后再试!";
}
try {
//等待5s,方便测试
Thread.sleep(5000);
if(dataCacheRepository.hasKey(KEY)){
Long aLong = dataCacheRepository.get(KEY);
dataCacheRepository.set(KEY,aLong+account,-1);
return account+aLong+"";
}else {
dataCacheRepository.set(KEY,account,-1);
return account+"";
}
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
return "程序运行异常,请联系管理员!";
} finally {
if (clientID.equals(dataCacheRepository.getByKey(userName))) {
log.info("finally删除锁{}", userName);
dataCacheRepository.unLock(userName);
}
}
}
}
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。
繁忙
配置
分布式
程序
稍后
帮助
服务
清楚
使用方法
内容
对此
文件
文章
新手
方法
时间
更多
服务器
知识
示例
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
橘子平台买的战地五是什么服务器
编程器软件开发
蜂窝网络与服务器连接中断
如何可以收缩数据库
安徽通信软件开发要多少钱
萍乡新成立网络安全办
软件开发费用算劳务
服务器dc测试和ac测试的区别
重庆邮电大学网络安全就业怎么样
小米 服务器请求失败
网络安全素质教育一级难吗
怀柔区管理软件开发一体化
阜阳大中小型视频系统服务器
苑fang弘网络安全
网络技术员年工作计划
sql数据库找用户名和密码
企业配方管理软件开发
微信软件开发人员职责
mysql数据库端
企业客户数据库模板下载吗
小程序新闻详情数据库
fgo选哪个服务器
山东网络安全中心招聘
cip数据库
绍兴希微网络技术公司
常用的电子图书数据库
网络安全工作的目标包括()
cf服务器显示器
定期更新数据库到内存
关于网络安全知识的视频培训