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

      




  • 相关阅读:
    Atitit 集团与个人的完整入口列表 attilax的完整入口 1. 集团与个人的完整入口列表 1 2. 流量入口概念 2 3. 流量入口的历史与发展 2 1.集团与个人的完整入口列表
    atitit 每季度日程表 每季度流程 v3 qaf.docx Ver history V2 add diary cyar data 3 cate V3 fix detail 3cate ,
    Atitit react 详细使用总结 绑定列表显示 attilax总结 1. 前言 1 1.1. 资料数量在百度内的数量对比 1 1.2. 版本16 v15.6.1 1 1.3. 引入js 2
    Atitit r2017 r3 doc list on home ntpc.docx
    Atitit r2017 ra doc list on home ntpc.docx
    Atiitt attilax掌握的前后技术放在简历里面.docx
    Atitit q2016 qa doc list on home ntpc.docx
    Atitit r7 doc list on home ntpc.docx 驱动器 D 中的卷是 p2soft 卷的序列号是 9AD0D3C8 D:\ati\r2017 v3 r01\
    Atitit 可移植性之道attilax著
    Atitit q2016 q5 doc list on home ntpc.docx
  • 原文地址:https://www.cnblogs.com/achengmu/p/14700800.html
Copyright © 2011-2022 走看看