使用redis和shedlock怎么实现分布式锁
发表于:2025-01-25 作者:千家信息网编辑
千家信息网最后更新 2025年01月25日,使用redis和shedlock怎么实现分布式锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1. jar包的引入
千家信息网最后更新 2025年01月25日使用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安全错误
数据库的锁怎样保障安全
市总工会网络安全宣传周
网络安全边界技术
海豚怎么选择服务器
英雄三国数据库
io处理服务器
sdn软件定义网络技术
辽宁推广软件开发资格
四川管理软件开发平台
网络安全和信息化工作典型人才
安全套市场环境分析数据库
网络安全国家一流学科
湛江通信软件开发零售价
服务器安全狗app
服务器设备功能
闵行区上门软件开发质量推荐
软件开发需求怎么联系
定做房卡棋牌软件开发
互联网与服务器之间的安全问题
网络安全审核多久
涪陵区一站式软件开发服务标志
烟台五八网络技术
数据库连接失败请检查网络连接
一年级下册网络安全手抄报
网络安全技术和管理
湖南金谱刷互联网科技有限公司
数据库管理系统可以查看文件吗
景区软件开发政策
2020软件开发好找工作吗
geo数据库如何查找疾病数据集
网络安全整体技术防护方案