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

    1. redis分布式锁

    1.1. 实现工具

    public class RedisTool {
     
        private static final String LOCK_SUCCESS = "OK";
        private static final String SET_IF_NOT_EXIST = "NX";
        private static final String SET_WITH_EXPIRE_TIME = "PX";
        private static final Long RELEASE_SUCCESS = 1L;
    
        /**
         * 尝试获取分布式锁
         * @param jedis Redis客户端
         * @param lockKey 锁
         * @param requestId 请求标识
         * @param expireTime 超期时间
         * @return 是否获取成功
         */
        public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
     
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
     
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
     
        }
    
    
        /**
         * 释放分布式锁
         * @param jedis Redis客户端
         * @param lockKey 锁
         * @param requestId 请求标识
         * @return 是否释放成功
         */
        public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
    
        }
     
    }
    

    1.2. redis配置

    package com.zhiyis.common.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    import java.lang.reflect.Method;
    
    
    @Configuration
    @EnableCaching
    public class RedisConfig extends CachingConfigurerSupport {
    
        private static Logger logger = LoggerFactory.getLogger(RedisConfig.class);
    
        @Value("${spring.redis.host}")
        private String redisHost;
    
        @Value("${spring.redis.port}")
        private int redisPort;
    
        @Value("${spring.redis.timeout}")
        private int redisTimeout;
    
        @Value("${spring.redis.password}")
        private String redisAuth;
    
        @Value("${spring.redis.database}")
        private int redisDb;
    
        @Value("${spring.redis.pool.max-active}")
        private int maxActive;
    
        @Value("${spring.redis.pool.max-wait}")
        private int maxWait;
    
        @Value("${spring.redis.pool.max-idle}")
        private int maxIdle;
    
        @Value("${spring.redis.pool.min-idle}")
        private int minIdle;
    
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override
                public Object generate(Object target, Method method, Object... params) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(target.getClass().getName());
                    sb.append(method.getName());
                    for (Object obj : params) {
                        sb.append(obj.toString());
                    }
                    return sb.toString();
                }
            };
        }
    
        @Bean
        public CacheManager redisCacheManager() {
            RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
            //默认300秒过期
            cacheManager.setDefaultExpiration(300);
            // 启动时加载远程缓存
            cacheManager.setLoadRemoteCachesOnStartup(true);
            //是否使用前缀生成器
            cacheManager.setUsePrefix(true);
            return cacheManager;
        }
    
    
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory() {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            poolConfig.setMaxTotal(maxActive);
            poolConfig.setMaxIdle(maxIdle);
            poolConfig.setMaxWaitMillis(maxWait);
            poolConfig.setMinIdle(minIdle);
            poolConfig.setTestOnBorrow(true);
            poolConfig.setTestOnReturn(false);
            poolConfig.setTestWhileIdle(true);
            JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
            jedisConnectionFactory.setPassword(redisAuth);
            jedisConnectionFactory.setHostName(redisHost);
            jedisConnectionFactory.setDatabase(redisDb);
            jedisConnectionFactory.setPort(redisPort);
            jedisConnectionFactory.setTimeout(redisTimeout);
            return jedisConnectionFactory;
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            Jackson2JsonRedisSerializer<Object> serializer = jackson2JsonRedisSerializer();
            redisTemplate.setConnectionFactory(redisConnectionFactory());
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(serializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(serializer);
            return redisTemplate;
        }
    
        @Bean
        public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
            final Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
            final ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
                    .json().build();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
            return jackson2JsonRedisSerializer;
        }
    
        @Bean
        public JedisPool redisPoolFactory() throws Exception {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxIdle(maxIdle);
            jedisPoolConfig.setMaxWaitMillis(maxWait);
            // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
            jedisPoolConfig.setBlockWhenExhausted(true);
            // 是否启用pool的jmx管理功能, 默认true
            jedisPoolConfig.setJmxEnabled(true);
            JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort, redisTimeout, redisAuth);
            return jedisPool;
        }
    }
    
    

    上述主要用到 redisPoolFactory方法,用来初始化jedispool,缓存等不需要用到可以删除

    1.3. 使用aop用注解的形式来进行分布式锁的包裹

    /**
     * redis分布式锁注解
     * @author laoliangliang
     * @date 2019/2/20 10:27
     */
    public @interface JedisLock {
    }
    
    package com.zhiyis.framework.aop;
    
    import com.zhiyis.common.utils.RedisTool;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    import java.lang.reflect.Method;
    import java.util.UUID;
    
    /**
     * redis分布式锁aop实现
     *
     * @author laoliangliang
     * @date 2019/1/10 17:04
     */
    @Slf4j
    @Component
    @Aspect
    public class JedisLockAspect {
    
        @Pointcut(value = "(execution(* *.*(..)) && @annotation(com.zhiyis.framework.annotation.JedisLock))")
        private void pointcut() {
        }
    
        @Autowired
        private JedisPool jedisPool;
    
        @Around("pointcut()")
        public Object lockAroud(ProceedingJoinPoint joinPoint) throws Throwable {
    
            //获取切入方法的数据
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取切入方法
            Method method = signature.getMethod();
            String lockKey = method.getName();
            String uuid = UUID.randomUUID().toString();
            try (Jedis jedis = jedisPool.getResource()) {
                boolean lock = RedisTool.tryGetDistributedLock(jedis, lockKey, uuid, 60000);
                if (lock) {
                    Object proceed = joinPoint.proceed();
                    RedisTool.releaseDistributedLock(jedis, lockKey, uuid);
                    return proceed;
                }
            } catch (Throwable e) {
                log.error("tradeCode接口错误", e);
                throw e;
            }
            return null;
        }
    
    
    }
    
    
    

    使用

    1. 使用就很简单了,在要使用分布式锁的方法上面直接加上 @JedisLock 注解

    参考 https://www.cnblogs.com/linjiqin/p/8003838.html

  • 相关阅读:
    6 November in 614
    4 November in ss
    标准模板库(STL)
    类模板
    函数模板和模板函数
    关于“宏定义”的作用范围
    运算符重载
    内存分配和释放的函数
    数据库恢复的基础是利用转储的冗余数据
    在局域网络内的某台主机用ping命令测试网络连接时发现网络内部的主机都可以连同,而不能与公网连通,问题可能是
  • 原文地址:https://www.cnblogs.com/sky-chen/p/10375142.html
Copyright © 2011-2022 走看看