使用redis和shedlock怎么实现分布式锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-redis-spring</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- swagger -->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
#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<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
public LockProvider lockProvider(RedisConnectionFactory connectionFactory) {
return new RedisLockProvider(connectionFactory);
}
@Bean
public ScheduledLockConfiguration taskSchedulerLocker(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder.withLockProvider(lockProvider)
.withPoolSize(tasksPoolSize).withDefaultLockAtMostFor(Duration.ofMinutes(lockMaxDuration))
.build();
}
}
package com.example.redis_demo_limit.redis;
public interface DataCacheRepository<T> {
boolean add(String collection, String hkey, T object, Long timeout);
boolean delete(String collection, String hkey);
T find(String collection, String hkey, Class<T> 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<T> implements com.example.redis_demo_limit.redis.DataCacheRepository<T> {
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<T> 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<String, Object> 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);
}
}
}
}
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。