Redis分散ロック--Spring-boot-start-data-redisベース


目次
1 build.gradle導入JAR   
 2  プロファイル:bootstrap-dev.yml
3 RedisConfig.java  
4 CacheService.java
現在Javaがredisを操作しているクライアントにはjedisとlettuceがあります.   springboot1.xシリーズではjedisが使用されているが、springboot2.xになるとLettuceが使用される.私たちのバージョンはspringboot2.xシリーズなので、今日はLettuceを使用しています.jedislettuceの違いについて:
  • LettuceとJedisの位置付けはすべてRedisのclientなので、もちろんredis serverに直接接続することができます.
  • Jedisは実装上直接接続されたredis serverであり、マルチスレッド環境で非スレッドが安全である場合、この場合は接続プールのみを使用してJedisインスタンスごとに物理接続
  • を追加する
  • Lettuceの接続はNettyに基づいており、接続インスタンス(StatefulRedisConnection)は複数のスレッド間で同時アクセス可能であり、StatefulRedisConnectionはスレッドが安全であるべきであるため、1つの接続インスタンス(StatefulRedisConnection)はマルチスレッド環境での同時アクセスを満たすことができ、もちろんこれも伸縮可能な設計であり、接続インスタンスが1つ足りない場合は、必要に応じて接続インスタンスを追加することもできます.

  • 1 build.gradle導入JAR   
     lettuceの使用
    compile "org.springframework.boot:spring-boot-starter-data-redis"
    implementation 'org.apache.commons:commons-pool2'

       spring-boot-start-data-redisを直接導入
      jedisの使用
    compile("org.springframework.boot:spring-boot-starter-data-redis"){
            exclude module: 'lettuce-core'
        }
    compile 'redis.clients:jedis:2.9.0'

    デフォルトではlettuceが使用され、jedisを使用する場合はexcludeを使用してlettuce-coreを除外し、jedisパッケージを導入できます.
    注意:redis依存commons-poolこの依存は必ず追加します
     2  プロファイル:bootstrap-dev.yml
       
      lettuceの使用
    server:
      port: 8989
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
        #           
        password: 123456
        #      jedis   lettuce  jedis  
        lettuce:
          pool:
            #           8
            max-active: 8
            #           8
            max-idle: 8
            #           0
            min-idle: 0

        jedisの使用
      redis:
        database: 0
        host: 127.0.0.1
        port: 30038
        password:
        timeout: 60000ms
        jedis:
          pool:
            #            (          )
            max-wait: -1ms
            #         (          )
            max-active: -1

    3 RedisConfig.java  
    redisのkeyとvalueのシーケンス化を構成する必要があります.デフォルトで使用されているJdkSerializationRedisSerializerは、redis desktop managerで表示されている私たちのkeyとvalueを通過するときに正常な文字ではないことを表示します.したがって、シーケンス化方式を手動で構成して、configパッケージを新規作成し、その次にRedisConfig.javaの具体的なコードを新規作成する必要があります.
    @Configuration
    @AutoConfigureAfter(RedisAutoConfiguration.class)
    public class RedisConfig {
    
        @Bean
        @SuppressWarnings("all")
        public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate template = new RedisTemplate();
            template.setConnectionFactory(factory);
            //  Jackson2JsonRedisSerializer         redis value 
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            // key  String      
            template.setKeySerializer(stringRedisSerializer);
            // hash key   String      
            template.setHashKeySerializer(stringRedisSerializer);
            // value       jackson
            template.setValueSerializer(jackson2JsonRedisSerializer);
            // hash value       jackson
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            template.afterPropertiesSet();
            return template;
        }
    }

    ここで、@Configurationはこのクラスが構成クラスであることを表し、@AutoConfigureAfter(RedisAutoConfiguration.class)は、この構成クラスを内蔵された構成クラス(RedisAutoConfiguration.class)の後に構成することで、構成クラスが有効になり、構成が上書きされないことを保証します.メソッド名は必ずredisTemplateと呼ぶ必要があります.なぜなら@Bean注釈はメソッド名に基づいてこのbeanのnameを構成しているからです.
     
    4 CacheService.java
    @Service
    public class CacheService {
    
        private static Logger logger = LoggerFactory.getLogger(CacheService.class);
    
        private static final Long SUCCESS = 1L;
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        /**
         *         
         *
         * @param key   
         * @param time   ( )
         * @return
         */
        public boolean expire(String key, long time) {
            try {
                if (time > 0) {
                    redisTemplate.expire(key, time, TimeUnit.SECONDS);
                }
                return true;
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         *   key       
         *
         * @param key      null
         * @return   ( )   0       
         */
    
        public long getExpire(String key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
    
        /**
         *   key    
         *
         * @param key  
         * @return true    false   
         */
        public boolean hasKey(String key) {
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         *     
         *
         * @param key           
         */
        @SuppressWarnings("unchecked")
        public void del(String... key) {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
        }
    
        /**
         *       
         *
         * @param key  
         * @return  
         */
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    
        /**
         *       
         *
         * @param key    
         * @param value  
         * @return true   false  
         */
        public boolean set(String key, Object value) {
            try {
                redisTemplate.opsForValue().set(key, value);
                return true;
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         *            
         *
         * @param key    
         * @param value  
         * @param time    ( ) time   0   time    0       
         * @return true   false   
         */
        public boolean set(String key, Object value, long time) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                } else {
                    set(key, value);
                }
                return true;
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         *   
         *
         * @param key    
         * @param delta     (  0)
         * @return
         */
        public long incr(String key, long delta) {
            if (delta < 0) {
                throw new RuntimeException("        0");
            }
            return redisTemplate.opsForValue().increment(key, delta);
        }
    
        /**
         *   
         *
         * @param key    
         * @param delta     (  0)
         * @return 147
         */
        public long decr(String key, long delta) {
            if (delta < 0) {
                throw new RuntimeException("        0");
            }
            return redisTemplate.opsForValue().increment(key, -delta);
        }
    
           /**
         *       
         * @param lockKey  
         * @param requestId     
         * @param expireTime    
         * @param waitTimeout     
         * @return       
         */
        public boolean tryLock(String lockKey, String requestId, int expireTime,long waitTimeout) {
            long nanoTime = System.nanoTime(); //     
            try{
                String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
    
                logger.info("        -key[{}]",lockKey);
                int count = 0;
                do{
                    RedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
    
                    logger.debug("        -key[{}]requestId[{}]count[{}]",lockKey,requestId,count);
                    Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),requestId,expireTime);
    
                    if(SUCCESS.equals(result)) {
                        logger.debug("        -key[{}]  ",lockKey);
                        return true;
                    }
    
                    Thread.sleep(500L);//  500  
                    count++;
                }while ((System.nanoTime() - nanoTime) < TimeUnit.MILLISECONDS.toNanos(waitTimeout));
    
            }catch(Exception e){
                logger.error("        -key[{}]  ",lockKey);
                logger.error(e.getMessage(),e);
            }
    
            return false;
        }
    
        /**
         *    
         * @param lockKey  
         * @param requestId     
         * @return       
         */
        public boolean releaseLock(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";
    
            RedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
    
            Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
            if (SUCCESS.equals(result)) {
                return true;
            }
    
            return false;
    
        }
    }
    

     コードアドレス:https://github.com/tengxvincent/spring-boot-vincent.git