RELATEED CONSULTING
相关咨询
选择下列产品马上在线沟通
服务时间:8:30-17:00
你可能遇到了下面的问题
关闭右侧工具栏

新闻中心

这里有您想知道的互联网营销解决方案
使用redis和shedlock怎么实现分布式锁

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

成都创新互联公司是一家专注于成都网站设计、做网站与策划设计,防城港网站建设哪家好?成都创新互联公司做网站,专注于网站建设十多年,网设计领域的专业建站公司;建站业务涵盖:防城港等地区。防城港做网站价格咨询:18982081108


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的配置

  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 redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate 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 {

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



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


新闻名称:使用redis和shedlock怎么实现分布式锁
本文链接:http://sczitong.cn/article/ijedjd.html