zoukankan      html  css  js  c++  java
  • redis锁,redis分布式锁: RedisLock

    最近在做一个项目,类型增减库存的,但是发现我的springboot版本太低,springboot1.5.9版本的,redis是2.9.0的。springboot2.x,redis3.x好的东西用不了。

    首先确定你的springboot版本,redis版本。

    1.如果不想考虑springboot,redis版本,那么用:Redisson分布式锁。

    Redisson分布式锁

    引入依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.7.5</version>
    </dependency>
    

      

    application.properties中的相关配置

    # Redis服务器地址(默认session使用)
    spring.redis.host=192.168.1.201
    # Redis服务器连接密码(默认为空)
    spring.redis.password=
    # Redis服务器连接端口
    spring.redis.port=6390
    

      

    定义一个Loker接口,用于分布式锁的一些操作

    import java.util.concurrent.TimeUnit;
    
    /**
     * 锁接口
     * @author jie.zhao
     */
    public interface Locker {
    
        /**
         * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
         *
         * @param lockKey
         */
        void lock(String lockKey);
    
        /**
         * 释放锁
         *
         * @param lockKey
         */
        void unlock(String lockKey);
    
        /**
         * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
         *
         * @param lockKey
         * @param timeout
         */
        void lock(String lockKey, int timeout);
    
        /**
         * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
         *
         * @param lockKey
         * @param unit
         * @param timeout
         */
        void lock(String lockKey, TimeUnit unit, int timeout);
    
        /**
         * 尝试获取锁,获取到立即返回true,未获取到立即返回false
         *
         * @param lockKey
         * @return
         */
        boolean tryLock(String lockKey);
    
        /**
         * 尝试获取锁,在等待时间内获取到锁则返回true,否则返回false,如果获取到锁,则要么执行完后程序释放锁,
         * 要么在给定的超时时间leaseTime后释放锁
         *
         * @param lockKey
         * @param waitTime
         * @param leaseTime
         * @param unit
         * @return
         */
        boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
                throws InterruptedException;
    
        /**
         * 锁是否被任意一个线程锁持有
         *
         * @param lockKey
         * @return
         */
        boolean isLocked(String lockKey);
    
    }
    

      

    实现类RedissonLocker,实现Locker中的方法

    import java.util.concurrent.TimeUnit;
    
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    
    /**
     * 基于Redisson的分布式锁
     * @author jie.zhao
     */
    public class RedissonLocker implements Locker {
    
        private RedissonClient redissonClient;
    
        public RedissonLocker(RedissonClient redissonClient) {
            super();
            this.redissonClient = redissonClient;
        }
    
        @Override
        public void lock(String lockKey) {
            RLock lock = redissonClient.getLock(lockKey);
            lock.lock();
        }
    
        @Override
        public void unlock(String lockKey) {
            RLock lock = redissonClient.getLock(lockKey);
            lock.unlock();
        }
    
        @Override
        public void lock(String lockKey, int leaseTime) {
            RLock lock = redissonClient.getLock(lockKey);
            lock.lock(leaseTime, TimeUnit.SECONDS);
        }
    
        @Override
        public void lock(String lockKey, TimeUnit unit, int timeout) {
            RLock lock = redissonClient.getLock(lockKey);
            lock.lock(timeout, unit);
        }
    
        public void setRedissonClient(RedissonClient redissonClient) {
            this.redissonClient = redissonClient;
        }
    
        @Override
        public boolean tryLock(String lockKey) {
            RLock lock = redissonClient.getLock(lockKey);
            return lock.tryLock();
        }
    
        @Override
        public boolean tryLock(String lockKey, long waitTime, long leaseTime,
                               TimeUnit unit) throws InterruptedException {
            RLock lock = redissonClient.getLock(lockKey);
            return lock.tryLock(waitTime, leaseTime, unit);
        }
    
        @Override
        public boolean isLocked(String lockKey) {
            RLock lock = redissonClient.getLock(lockKey);
            return lock.isLocked();
        }
    
    }
    

      

    工具类LockUtil

    import java.util.concurrent.TimeUnit;
    
    /**
     * redis分布式锁工具类
     * @author jie.zhao
     */
    public final class LockUtil {
    
        private static Locker locker;
    
        /**
         * 设置工具类使用的locker
         *
         * @param locker
         */
        public static void setLocker(Locker locker) {
            LockUtil.locker = locker;
        }
    
        /**
         * 获取锁
         *
         * @param lockKey
         */
        public static void lock(String lockKey) {
            locker.lock(lockKey);
        }
    
        /**
         * 释放锁
         *
         * @param lockKey
         */
        public static void unlock(String lockKey) {
            locker.unlock(lockKey);
        }
    
        /**
         * 获取锁,超时释放
         *
         * @param lockKey
         * @param timeout
         */
        public static void lock(String lockKey, int timeout) {
            locker.lock(lockKey, timeout);
        }
    
        /**
         * 获取锁,超时释放,指定时间单位
         *
         * @param lockKey
         * @param unit
         * @param timeout
         */
        public static void lock(String lockKey, TimeUnit unit, int timeout) {
            locker.lock(lockKey, unit, timeout);
        }
    
        /**
         * 尝试获取锁,获取到立即返回true,获取失败立即返回false
         *
         * @param lockKey
         * @return
         */
        public static boolean tryLock(String lockKey) {
            return locker.tryLock(lockKey);
        }
    
        /**
         * 尝试获取锁,在给定的waitTime时间内尝试,获取到返回true,获取失败返回false,获取到后再给定的leaseTime时间超时释放
         *
         * @param lockKey
         * @param waitTime
         * @param leaseTime
         * @param unit
         * @return
         * @throws InterruptedException
         */
        public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
                                      TimeUnit unit) throws InterruptedException {
            return locker.tryLock(lockKey, waitTime, leaseTime, unit);
        }
    
        /**
         * 锁释放被任意一个线程持有
         *
         * @param lockKey
         * @return
         */
        public static boolean isLocked(String lockKey) {
            return locker.isLocked(lockKey);
        }
    }
    

      

    redisson的配置类RedissonConfig

    import java.io.IOException;
    
    import com.rxjy.common.redissonlock.LockUtil;
    import com.rxjy.common.redissonlock.RedissonLocker;
    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    import org.redisson.config.SingleServerConfig;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    @Configuration
    public class RedissonConfig {
    
        @Value("${spring.redis.database}")
        private int database;
        @Value("${spring.redis.host}")
        private String host;
        @Value("${spring.redis.port}")
        private String port;
        @Value("${spring.redis.password}")
        private String password;
        @Value("${spring.redis.timeout}")
        private int timeout;
    
        /**
         * RedissonClient,单机模式
         *
         * @return
         * @throws IOException
         */
        @Bean(destroyMethod = "shutdown")
        public RedissonClient redisson() {
            Config config = new Config();
            SingleServerConfig singleServerConfig = config.useSingleServer();
            singleServerConfig.setAddress("redis://" + host + ":" + port);
            singleServerConfig.setTimeout(timeout);
            singleServerConfig.setDatabase(database);
            if (password != null && !"".equals(password)) { //有密码
                singleServerConfig.setPassword(password);
            }
            return Redisson.create(config);
        }
    
        @Bean
        public RedissonLocker redissonLocker(RedissonClient redissonClient) {
            RedissonLocker locker = new RedissonLocker(redissonClient);
            //设置LockUtil的锁处理对象
            LockUtil.setLocker(locker);
            return locker;
        }
    }
    

      

    测试:

    import com.rxjy.common.redissonlock.LockUtil;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RedissonLockTest {
    
        static final String KEY = "LOCK_KEY";
    
        @GetMapping("/test")
        public Object test(){
            //加锁
            LockUtil.lock(KEY);
            try {
                //TODO 处理业务
                System.out.println(" 处理业务。。。");
            } catch (Exception e) {
                //异常处理
            }finally{
                //释放锁
                LockUtil.unlock(KEY);
            }
    
            return "SUCCESS";
        }
    }
    

      

    注意:Redisson中的key,不能有数字,分号,冒号等特殊字符。

    可以是这样:test-lock

    2.springboot2.x, redis3.x 自己编写RedisLock

    RedisLock

    VariableKeyLock.java

    import java.util.concurrent.locks.Lock;
    
    /**
     * 可变key锁
     *
     */
    public interface VariableKeyLock extends Lock {
    
        boolean tryLock(String key);
    
        void lock(String key);
    
        void unlock(String key);
    }
    

      

    RedisLock.java
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.stereotype.Component;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisCluster;
    
    import java.util.Arrays;
    import java.util.Random;
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    
    /**
     * redis锁
     *
     */
    @Component
    @Slf4j
    public class RedisLock implements VariableKeyLock {
    
        public static final String LOCK = "LOCK";
    
        @Autowired
        private RedisConnectionFactory factory;
    
        private ThreadLocal<String> localValue = new ThreadLocal<String>();
    
        /**
         * 解锁lua脚本
         */
        private static final String UNLOCK_LUA =
            "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end";
    
        @Override
        public void lock() {
            if (!tryLock()) {
                try {
                    Thread.sleep(new Random().nextInt(10) + 1);
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                }
                lock();
            }
        }
    
        @Override
        public void lock(String key) {
            if (!tryLock(key)) {
                try {
                    Thread.sleep(new Random().nextInt(10) + 1);
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                }
                lock(key);
            }
        }
    
        @Override
        public boolean tryLock() {
            RedisConnection connection = null;
            try {
                connection = factory.getConnection();
                Jedis jedis = (Jedis)connection.getNativeConnection();
                String value = UUID.randomUUID().toString();
                localValue.set(value);
                String ret = jedis.set(LOCK, value, "NX", "PX", 10000);
                return ret != null && "OK".equals(ret);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
            return false;
        }
    
        @Override
        public boolean tryLock(String key) {
            RedisConnection connection = null;
            try {
                connection = factory.getConnection();
                Jedis jedis = (Jedis)connection.getNativeConnection();
                String value = UUID.randomUUID().toString();
                localValue.set(value);
                String ret = jedis.set(key, value, "NX", "PX", 10000);
                return ret != null && "OK".equals(ret);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
            return false;
        }
    
        @Override
        public void unlock() {
            String script = UNLOCK_LUA;
            RedisConnection connection = null;
            try {
                connection = factory.getConnection();
                Object jedis = connection.getNativeConnection();
                if (jedis instanceof Jedis) {
                    ((Jedis)jedis).eval(script, Arrays.asList(LOCK), Arrays.asList(localValue.get()));
                } else if (jedis instanceof JedisCluster) {
                    ((JedisCluster)jedis).eval(script, Arrays.asList(LOCK), Arrays.asList(localValue.get()));
                }
    
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
    
        }
    
        @Override
        public void unlock(String key) {
            String script = UNLOCK_LUA;
            RedisConnection connection = null;
            try {
                connection = factory.getConnection();
                Object jedis = connection.getNativeConnection();
                if (jedis instanceof Jedis) {
                    ((Jedis)jedis).eval(script, Arrays.asList(key), Arrays.asList(localValue.get()));
                } else if (jedis instanceof JedisCluster) {
                    ((JedisCluster)jedis).eval(script, Arrays.asList(key), Arrays.asList(localValue.get()));
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        }
    
        // -------------------------------------
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        @Override
        public Condition newCondition() {
            return null;
        }
    
    }
    

      

    RedisConfig.java配置文件
    import com.alibaba.fastjson.parser.ParserConfig;
    import com.xxx.common.support.FastJsonSerializationRedisSerializer;
    import com.xxx.common.support.log.LogRedisReceiver;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisClusterConfiguration;
    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.jedis.JedisClientConfiguration;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.core.*;
    import org.springframework.data.redis.listener.PatternTopic;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import redis.clients.jedis.JedisPoolConfig;
    
    import java.time.Duration;
    import java.util.Optional;
    
    @Configuration
    public class RedisConfig {
    
        @Autowired
        private RedisProperties redisProperties;
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        /**
         * 实例化 RedisTemplate 对象
         *
         * @return
         */
        @Bean
        public RedisTemplate<String, Object> functionDomainRedisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            initDomainRedisTemplate(redisTemplate, connectionFactory());
            return redisTemplate;
        }
    
        /**
         * 设置数据存入 redis 的序列化方式
         *
         * @param redisTemplate
         * @param factory
         */
        private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new FastJsonSerializationRedisSerializer(Object.class));
            //		redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            //		redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
            redisTemplate.setHashValueSerializer(new FastJsonSerializationRedisSerializer(Object.class));
    
            ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    
            redisTemplate.setConnectionFactory(factory);
        }
    
        /**
         * 实例化 HashOperations 对象,可以使用 Hash 类型操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForHash();
        }
    
        /**
         * 实例化 ValueOperations 对象,可以使用 String 操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForValue();
        }
    
        /**
         * 实例化 ListOperations 对象,可以使用 List 操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForList();
        }
    
        /**
         * 实例化 SetOperations 对象,可以使用 Set 操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForSet();
        }
    
        /**
         * 实例化 ZSetOperations 对象,可以使用 ZSet 操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForZSet();
        }
    
        @Bean
        public RedisConnectionFactory connectionFactory() {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            if (redisProperties.getJedis().getPool() != null) {
                poolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
                poolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
                poolConfig.setMaxWaitMillis(redisProperties.getJedis().getPool().getMaxWait().toMillis());
                poolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
            }
            poolConfig.setTestOnBorrow(true);
            poolConfig.setTestOnReturn(false);
            poolConfig.setTestWhileIdle(true);
    
            JedisClientConfiguration jedisClientConfiguration;
            jedisClientConfiguration = JedisClientConfiguration.builder().usePooling().poolConfig(poolConfig).and()
                .readTimeout(Optional.ofNullable(redisProperties.getTimeout()).orElse(Duration.ofMillis(5000))).build();
            // 如果集群配置未空,则是单机。否则是集群
            if (redisProperties.getCluster() == null || CollectionUtils.isEmpty(redisProperties.getCluster().getNodes())) {
                RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
                redisStandaloneConfiguration.setDatabase(redisProperties.getDatabase());
                redisStandaloneConfiguration.setPort(redisProperties.getPort());
                redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
                redisStandaloneConfiguration.setHostName(redisProperties.getHost());
                return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
            } else {
                RedisClusterConfiguration redisClusterConfiguration =
                    new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
                redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
                return new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
            }
        }
    
        @Bean
        public RedisMessageListenerContainer container(MessageListenerAdapter listenerAdapter) {
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory());
            container.addMessageListener(listenerAdapter, new PatternTopic(applicationName + "-log"));
            return container;
        }
    
        @Bean
        public MessageListenerAdapter listenerAdapter(LogRedisReceiver logRedisReceiver) {
            return new MessageListenerAdapter(logRedisReceiver, "receiveMessage");
        }
    }
    

      

    application.propeties的redis配置,参考spring.redis.*的配置

    测试:

          redisLock.lock(RedisKeyConstants.SECKILL_PREX + id );
            try{
                log.debug("increaseSeckillStock-减库存: seckillId-" + seckillId + ",id-"+id);
                try{
                    maxPdfNo = seckillInventoryService.deductionInventory(id, seckillId, seckillNo, passengerNum);
                }catch (Exception e){
                    e.printStackTrace();
                    log.debug("deductionSeckillStock-减库存:" + e.getMessage());
                }
            }finally {
                redisLock.unlock(RedisKeyConstants.SECKILL_PREX + id);
            }
    

      




  • 相关阅读:
    node.js中常用的fs文件系统
    秒懂 this
    Filter 过滤器
    Ubuntu 安装zookeeper
    Vmware 设置NAT模式
    TreeMap
    ArrayList扩容
    Java 面试题收集
    SwitchyOmega 设置修改代理
    Jedis操作Redis
  • 原文地址:https://www.cnblogs.com/achengmu/p/14700800.html
Copyright © 2011-2022 走看看