收藏慢慢看系列:简洁实用的Redis分布式锁用法

在微服务中不少状况下须要使用到分布式锁功能,而目前比较常见的方案是经过Redis来实现分布式锁,网上关于分布式锁的实现方式有不少,早期主要是基于Redisson等客户端,但在Spring Boot2.x以上版本中使用Redis时,其客户端库已经默认使用lettuce。 因此本文将直接介绍在Spring Boot2.x以上项目中快速使用Redis分布式锁的功能的方法,但愿可以更新你的知识库!redis

Redis分布式锁原理概述

实际上Redis服务自己并不提供分布式锁这样的机制,可是做为全局Key-Value存储系统,客户端能够利用Redis提供的基本功能并经过必定的算法设计来实现分布式锁功能。目前已有很多博客文章及代码库描述了如何使用Redis来实现分布式锁,可是许多实现相对比较简单,安全性也比较低。在Redis的官方文档中推荐了一种叫作RedLock的算法来实现基于Redis的分布式锁功能,现阶段已存在基于该算法的多种语言版本的Redis客户端实现库。其中Java领域最为知名的是Redisson库。但因为Redisson不只实现了分布式锁功能,还额外实现了一套Redis分布式数据结构,所以会显得比较重,加上最新的基于Spring Boot.2.x以上版本使用Redis时,其客户端库已经默认使用了lettuce(比Redisson、Jedis线程更安全、更轻量级的一种Java Redis客户端库)的封装,因此为了更加符合微服务场景下的使用,在实践中每每会选择基于RedLock算法自行实现分布式锁。算法

本案例也将演示如何RedLock算法来实现Redis分布式锁功能,不过在此以前让咱们先来看看RedLock算法是如何运行的,示意图以下:spring

以上就是实现Redis分布式锁官方推荐的RedLock算法逻辑,它是一种多节点Redis的分布式锁算法,能够有效防止单节点故障问题。其执行步骤说明以下:安全

  • 首先Redis客户端获取当前系统时间,以毫秒为单位;
  • 而后客户端会顺序地尝试向Redis集群中的每一个节点获取锁,其具体步骤是使用相同的键Key名和随机值;在向每一个Redis节点获取锁的过程当中,客户端会以比锁过时时间小得多的时间来设定超时机制,例如锁的整个超时时间为10秒,集群有5个节点,那么每一个节点获取锁的超时时间可能会被限制在5~50毫秒之间,这是为了防止在某个节点不可用的状况下,客户端等待时间过长,形成性能阻塞;
  • 以后随着各节点获取锁结果的反馈,Redis客户端会对获取状况进行判断,若是获取各节点锁的总时间小于锁的超时时间设置,而且成功获取锁的节点数目大于N/2+1个(例如5个节点至少要有3个节点成功获取锁),知足上述条件的状况下,Redis客户端才会认为获取锁成功,不然就会认为锁获取失败,并依次释放掉各个节点的锁信息;
  • 获取锁成功后便可以安全地执行操做,完成后再依次释放各节点锁持有的锁信息;

实现上述算法的Redis客户端能够基本上保证分布式锁的有效性及安全性的几个基本特性要求:bash

  • 互斥:任什么时候刻只能有一个Redis客户端获取锁;
  • 无死锁:即便锁定资源的服务崩溃或者分区,仍然能释放锁);
  • 容错性:只要多数redis节点(一半以上)在使用,Redis客户端就能够获取和释放锁;

Spring Boot集成使用方式

经过前面内容的描述,相信你对实现Redis分布式锁的基本算法应该有了必定的认识和理解。而在实践的过程当中能够依据该算法自行定制实现,但实际上Spring早就提供了基于该算法的Redis的分布式锁的实现。其具体使用步骤以下:数据结构

1)在工程pom.xml文件中引入Spring Integration依赖,代码以下:分布式

<!-- spring integration -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<!-- spring integration与redis结合,实现redis分布式锁 -->
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-redis</artifactId>
</dependency>
复制代码

目前Spring所提供的分布式锁相关的代码被迁移在Spring Integration子项目中,因此这里引入其相关依赖。ide

2)编写RedisLock的配置类,代码以下:spring-boot

@Configuration
public class RedisLockConfiguration {
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        return new RedisLockRegistry(redisConnectionFactory, "payment");
    }
}
复制代码

以上配置代码加载的前提在于应用已经集成了Redis服务访问连接信息,具体Spring Boot项目集成Redis访问的方式比较简单能够参考其余资料。微服务

3)分布式锁的具体使用方式,代码片断以下:

/**
 * 引入Redis分布式锁依赖组件
 */
@Autowired
private RedisLockRegistry redisLockRegistry;

@Override
public UnifiedPayBO unifiedPay(UnifiedPayDTO unifiedPayDTO) {
    ...
    //建立Redis分布式锁
    Lock lock = redisLockRegistry.obtain(redisLockPrefix + unifiedPayDTO.getOrderId());
    try {      
        //尝试获取锁
        boolean isLock = lock.tryLock(1, TimeUnit.SECONDS);
        if (isLock) {
            //执行业务逻辑
            ...
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        //释放分布式锁
        lock.unlock();
    }
    ...
}
复制代码

上述代码为订单防重时使用Redis分布锁的示例代码,经过依赖注入RedisLockRegistry实例来实现分布式锁的相关操做,例如obtain()方法建立锁、tryLock()持有锁及unlock()释放锁等。

写在最后:

欢迎你们关注我新开通的公众号【风平浪静如码】,海量Java相关文章,学习资料都会在里面更新,整理的资料也会放在里面。

以为写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

转载自公众号:mp.weixin.qq.com/s/AAq5Eu6x_…

相关文章
相关标签/搜索