温馨提示×

温馨提示×

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

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

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

发布时间:2021-08-09 14:42:30 来源:亿速云 阅读:327 作者:Leah 栏目:大数据

使用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. 配置文件
#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
 
  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;

@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();
    }
}

 
  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
@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);
            }
        }
    }
}



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

向AI问一下细节

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

AI