• 高性能分布式锁-redisson的使用


    1,概述:在一些高并发的场景中,比如秒杀,抢票,抢购这些场景,都存在对核心资源,商品库存的争夺,控制不好,库存数量可能被减少到负数,出现超卖的情况,或者 产生唯一的一个递增ID,由于web应用部署在多个机器上,简单的同步加锁是无法实现的,给数据库加锁的话,对于高并发,1000/s的并发,数据库可能由行锁变成表锁,性能下降会厉害。那相对而言,redis的分布式锁,相对而言,是个很好的选择,redis官方推荐使用的Redisson就提供了分布式锁和相关服务。 
    下面介绍下如何使用Redisson。

    2,Redisson的使用方式十分简单,详见官方文档:https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95

    3,加入jar包的依赖:

    <--  和RedisTemplate整合 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.10.2</version>
    </dependency>
    <dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-data-20</artifactId>
    <version>3.10.2</version>
    </dependency>

    4,配置Redisson

    yml配置
    redis:
    jedis:
    pool:
    max-active: 8
    max-idle: 8
    min-idle: 0
    max-wait: -1
    database: 0
    timeout: 2000
    password:
    #方式一: 集群
    cluster:
    nodes: 192.168.56.99:6379,192.168.59.100:6379
      #方式二: 单机  二选一
      port: 6379
    host: 192.168.56.99
     
    package com.chitic.supplywater.update.config.redis;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.AllArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    import org.redisson.spring.data.connection.RedissonConnectionFactory;
    import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
    import org.springframework.cache.Cache;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.CacheErrorHandler;
    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.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    @Configuration
    @EnableCaching
    @Slf4j
    @AllArgsConstructor
    public class RedissonConfig {
    
        private final RedisProperties redisProperties;
    
        @Bean
        public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
            return new RedissonConnectionFactory(redisson);
        }
    
        @Bean
        public RedissonClient redissonClient() {
            Config config = new Config();
            //redis服务器地址,多个逗号分隔
            RedisProperties.Cluster cluster = redisProperties.getCluster();
            List<String> nodes = cluster.getNodes();
            Set<String> nodeSet = new HashSet<>();
            for (String node : nodes) {
                nodeSet.add("redis://" + node);
            }
            //单机
            config.useSingleServer()
                    .setAddress("redis://" + nodes.get(0))
                    ;
            //集群
            //config.useClusterServers()
            //        .setScanInterval(2000)
            //        .addNodeAddress(nodeSet.toArray(new String[nodeSet.size()]))
            //        ;
            return Redisson.create(config);
        }
    
        @Bean
        public KeyGenerator keyGenerator() {
            //  设置自动key的生成规则,配置spring boot的注解,进行方法级别的缓存
            // 使用:进行分割,可以很多显示出层级关系
            // 这里其实就是new了一个KeyGenerator对象,只是这是lambda表达式的写法,我感觉很好用,大家感兴趣可以去了解下
            return (target, method, params) -> {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(":");
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(":" + String.valueOf(obj));
                }
                String rsToUse = String.valueOf(sb);
                log.info("自动生成Redis Key -> [{}]", rsToUse);
                return rsToUse;
            };
        }
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
            // 初始化缓存管理器,在这里我们可以缓存的整体过期时间什么的,我这里默认没有配置
            log.info("初始化 -> [{}]", "CacheManager RedisCacheManager Start");
            RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
                    .RedisCacheManagerBuilder
                    .fromConnectionFactory(redisConnectionFactory);
            return builder.build();
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory ) {
            //设置序列化
            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);
            // 配置redisTemplate
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            RedisSerializer stringSerializer = new StringRedisSerializer();
            redisTemplate.setKeySerializer(stringSerializer); // key序列化
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value序列化
            redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // Hash value序列化
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Bean
        public CacheErrorHandler errorHandler() {
            // 异常处理,当Redis发生异常时,打印日志,但是程序正常走
            log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
            CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
                @Override
                public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                    log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
                }
    
                @Override
                public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                    log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
                }
    
                @Override
                public void handleCacheEvictError(RuntimeException e, Cache cache, Object key)    {
                    log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
                }
    
                @Override
                public void handleCacheClearError(RuntimeException e, Cache cache) {
                    log.error("Redis occur handleCacheClearError:", e);
                }
            };
            return cacheErrorHandler;
        }
    
    }
    
    
    
     

    5,锁的获取和释放

    public class DistributedRedisLock {
       //从配置类中获取redisson对象
        private static Redisson redisson = RedissonManager.getRedisson();
        private static final String LOCK_TITLE = "redisLock_";
       //加锁
        public static boolean acquire(String lockName){
           //声明key对象
            String key = LOCK_TITLE + lockName;
           //获取锁对象
            RLock mylock = redisson.getLock(key);
           //加锁,并且设置锁过期时间,防止死锁的产生
            mylock.lock(2, TimeUnit.MINUTES); 
            System.err.println("======lock======"+Thread.currentThread().getName());
           //加锁成功
            return  true;
        }
      //锁的释放
        public static void release(String lockName){
           //必须是和加锁时的同一个key
            String key = LOCK_TITLE + lockName;
           //获取所对象
            RLock mylock = redisson.getLock(key);
          //释放锁(解锁)
            mylock.unlock();
            System.err.println("======unlock======"+Thread.currentThread().getName());
        }
    }

    6,业务逻辑中使用分布式锁

    @RequestMapping("/redder")
        @ResponseBody
        public String redder() throws IOException{
             String key = "test123";
            //加锁 
            DistributedRedisLock.acquire(key);
            //执行具体业务逻辑
            dosoming
            //释放锁
            DistributedRedisLock.release(key);
           //返回结果
            return soming; 
        }
  • 相关阅读:
    python操作mysql封装成类
    es 数据 导出 到 MySQL
    Elasticsearch的数据导出和导入操作(elasticdump工具),以及删除指定type的数据(delete-by-query插件)
    解决VM虚拟机中的ubuntu不能全屏的问题
    pandas操作,感觉不错,复制过来的
    BTree和B+Tree详解
    ant安装配置
    jmeter默认生成测试报告
    学习网站
    selenium多窗口切换(windows)
  • 原文地址:https://www.cnblogs.com/gaomanito/p/10729875.html
Copyright © 2020-2023  润新知