使用redis和shedlock怎么实现分布式锁
发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,使用redis和shedlock怎么实现分布式锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1. jar包的引入
千家信息网最后更新 2026年11月02日使用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安全错误
数据库的锁怎样保障安全
南京欣网互联网络科技
什么是科技互联网
互联网科技工作委员会副主任毛军
互联网科技活动banner
深圳恒合互联网络科技有限公司
互联网科技社
猪兼强互联网科技
上海挚品互联网科技
互联网科技排名
互联网科技论坛
互联网科技视频素材
华中科技有哪些互联网大佬
湖南洣水互联网科技有限公司扣款
互联网科技教育视频
互联网软件和科技
互联网媒体平台算高科技产业
燊美集互联网科技有限公司
徐州市互联网科技有限公司
上海同邻互联网科技有限公司规模
互联网时代科技型公司的机会
互联网 科技
南京欣网互联网科技
互联网金融科技平台
互联网 视角下的科技创新
恒生科技互联网指数分析
互联网经济与金融科技
郑州欣桑互联网科技有限公司
广州燕处互联网科技有限公司
江苏互联网智能科技产品出厂价
青岛互联网科技园