胖胖的枫叶
主页
博客
知识图谱
产品设计
数据分析
企业架构
项目管理
效率工具
全栈开发
后端
前端
测试
运维
数据
面试
  • openJdk-docs
  • spring-projects-docs
  • mysql-docs
  • redis-commands
  • redis-projects
  • apache-rocketmq
  • docker-docs
  • mybatis-docs
  • netty-docs
  • journaldev
  • geeksforgeeks
  • 浮生若梦
  • 后端进阶
  • 并发编程网
  • 英语肌肉记忆锻炼软件
  • 墨菲安全
  • Redisson-docs
  • jmh-Visual
  • 美团技术
  • MavenSearch
主页
博客
知识图谱
产品设计
数据分析
企业架构
项目管理
效率工具
全栈开发
后端
前端
测试
运维
数据
面试
  • openJdk-docs
  • spring-projects-docs
  • mysql-docs
  • redis-commands
  • redis-projects
  • apache-rocketmq
  • docker-docs
  • mybatis-docs
  • netty-docs
  • journaldev
  • geeksforgeeks
  • 浮生若梦
  • 后端进阶
  • 并发编程网
  • 英语肌肉记忆锻炼软件
  • 墨菲安全
  • Redisson-docs
  • jmh-Visual
  • 美团技术
  • MavenSearch
  • 博客

    • 博客迁移说明
    • 2024年
    • 2023年
    • 2022年
    • 2021年
    • 2020年
    • 2019年
    • 2018年

Redisson

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

代码演示

RedLock

Redlock 算法的基本思路,是让客户端和多个独立的 Redis 实例依次请求加锁,如果客户端能够和半数以上的实例成功地完成加锁操作,就认为客户端成功地获得分布式锁了,否则加锁失败。即使有单个 Redis 实例发生故障,因为锁变量在其它实例上也有保存,所以,客户端仍然可以正常地进行锁操作,锁变量并不会丢失。

  • 操作流程
  1. 客户端获取当前时间

  2. 客户端按照N个Redis实例执行加锁操作

    • 加锁操作和在单实例上执行的加锁操作一样,使用 SET 命令,带上 NX,EX/PX 选项,以及带上客户端的唯一标识。还有一个超时时间。如果在某一个Redis实例上加锁失败,会直接在下一个实例上执行。加锁操作的超时时间需要远远地小于锁的有效时间,一般也就是设置为几十毫秒。

  3. 客户端完成了和所有 Redis 实例的加锁操作,客户端就要计算整个加锁过程的总耗时。

    • 当客户端加锁操作满足,以下条件才算成功。
      1. 客户端从超过半数(大于等于 N/2+1)的 Redis 实例上成功获取到了锁;
      2. 客户端获取锁的总耗时没有超过锁的有效时间。
  4. 客户端释放锁

    • 释放掉锁执行lua脚本即可。

Redisson实现RedLock

  • Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

Redis主从版本

/**
 * @author z201.coding@gmail.com
 **/
@Configuration
public class RedisConfig {

    @Bean
    public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
        return new RedissonConnectionFactory(redisson);
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // key序列化方式
        redisTemplate.setKeySerializer(redisSerializer);
        // value序列化
        redisTemplate.setValueSerializer(redisSerializer);
        // value hashmap序列化
        redisTemplate.setHashValueSerializer(redisSerializer);
        return redisTemplate;
    }

    @Bean
    public RedissonClient redissonClient() throws IOException {
        Config config = new Config();
        // 主从
        config.useMasterSlaveServers()
                //可以用"rediss://"来启用SSL连接
                .setMasterAddress("redis://127.0.0.1:6379").setPassword("redis_pwd")
                .addSlaveAddress("redis://127.0.0.1:6380","redis://127.0.0.1:6381").setPassword("redis_pwd")
                .setRetryInterval(5000)
                .setTimeout(10000)
                .setConnectTimeout(10000);//(连接超时,单位:毫秒 默认值:3000);
        return Redisson.create(config);
    }
}

  • 单元测试
@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = AppApplication.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AppApplicationTest {

    @Autowired
    private RedissonClient redissonClient;

    private static final String LOCK_TITLE = "redisLock_";

    @Test
    @Disabled
    void contextLoads() throws InterruptedException {
        int count = 10;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        ExecutorService executorService = Executors.newFixedThreadPool(count);
        String key = UUID.randomUUID().toString();
        String lockKey = LOCK_TITLE + key;
        for (int i = 0; i < count; i++) {
            executorService.execute(() -> {
                try {
                    RLock lock = redissonClient.getLock(lockKey);
                    //加锁,并且设置锁过期时间,防止死锁的产生
                    Boolean result = lock.tryLock(2, TimeUnit.MINUTES);
                    log.info(" {} lock {}", result, lockKey);
                    Thread.sleep(1000L);
                    //执行具体业务逻辑
                    redissonClient.getBucket("value").set(String.valueOf(System.currentTimeMillis()));
                    String value = (String) redissonClient.getBucket("value").get();
                    log.info(" {} value {}", Thread.currentThread().getName(), value);
                } catch (Exception e) {
                    log.error("{}", e.getMessage());
                } finally {
                    //获取所对象
                    RLock lock = redissonClient.getLock(lockKey);
                    //释放锁(解锁)
                    lock.unlock();
                    log.info(" {} unlock {}", Thread.currentThread().getName(), lockKey);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.countDown();
        executorService.shutdown();
        ;
        log.info("run end~~~");
        Thread.sleep(11000L);
    }
}

END

Last Updated:
Contributors: 庆峰