温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

使用redis和shedlock怎么实现分布式锁

发布时间:2021-08-09 14:42:30 阅读:351 作者:Leah 栏目:大数据
开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

使用redis和shedlock怎么实现分布式锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。


1. jar包的引入

<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>
   

2. redis的配置

  1. 配置文件
#redisredis.host=192.168.1.6redis.password=redis.port=6379redis.taskScheduler.poolSize=100redis.taskScheduler.defaultLockMaxDurationMinutes=10redis.default.timeout=10redisCache.expireTimeInMilliseconds=1200000
 
  1. 配置类
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;@Configurationpublic 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();    }}
 
  1. 操作类
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@Repositorypublic 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);            }        }    }}

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:https://my.oschina.net/u/4607047/blog/4651562

AI

开发者交流群×