这样实现分布式锁,才叫优雅!

简介: 这样实现分布式锁,才叫优雅!

概述


提到分布式锁大家都会想到如下两种:

  • 基于Redisson组件,使用redlock算法实现
  • 基于Apache Curator,利用Zookeeper的临时顺序节点模型实现

今天我们来说说第三种,使用 Spring Integration 实现,也是我个人比较推荐的一种。

Spring Integration在基于Spring的应用程序中实现轻量级消息传递,并支持通过声明适配器与外部系统集成。Spring Integration的主要目标是提供一个简单的模型来构建企业集成解决方案,同时保持关注点的分离,这对于生成可维护,可测试的代码至关重要。我们熟知的 Spring Cloud Stream的底层就是Spring Integration。

官方地址:https://github.com/spring-projects/spring-integration

Spring Integration提供的全局锁目前为如下存储提供了实现:

  • Gemfire
  • JDBC
  • Redis
  • Zookeeper

它们使用相同的API抽象,这意味着,不论使用哪种存储,你的编码体验是一样的。试想一下你目前是基于zookeeper实现的分布式锁,哪天你想换成redis的实现,我们只需要修改相关依赖和配置就可以了,无需修改代码。下面是你使用 Spring Integration 实现分布式锁时需要关注的方法:

方法名 描述
lock() Acquires the lock. 加锁,如果已经被其他线程锁住或者当前线程不能获取锁则阻塞
lockInterruptibly() Acquires the lock unless the current thread is interrupted. 加锁,除非当前线程被打断。
tryLock() Acquires the lock only if it is free at the time of invocation. 尝试加锁,如果已经有其他锁锁住,获取当前线程不能加锁,则返回false,加锁失败;加锁成功则返回true
tryLock(long time, TimeUnit unit) Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted. 尝试在指定时间内加锁,如果已经有其他锁锁住,获取当前线程不能加锁,则返回false,加锁失败;加锁成功则返回true
unlock() Releases the lock. 解锁


实战


话不多说,我们看看使用 Spring Integration 如何基于redis和zookeeper快速实现分布式锁,至于Gemfire 和 Jdbc的实现大家自行实践。


基于Redis实现

  • 引入相关组件
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.integration</groupId>
 <artifactId>spring-integration-redis</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 在application.yml中添加redis的配置
spring:
 redis:
  host: 172.31.0.149
  port: 7111
  • 建立配置类,注入RedisLockRegistry
@Configuration
public class RedisLockConfiguration {
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory){
        return new RedisLockRegistry(redisConnectionFactory, "redis-lock");
    }
}
  • 编写测试代码
@RestController
@RequestMapping("lock")
@Log4j2
public class DistributedLockController {
    @Autowired
    private RedisLockRegistry redisLockRegistry;
    @GetMapping("/redis")
    public void test1() {
        Lock lock = redisLockRegistry.obtain("redis");
        try{
            //尝试在指定时间内加锁,如果已经有其他锁锁住,获取当前线程不能加锁,则返回false,加锁失败;加锁成功则返回true
            if(lock.tryLock(3, TimeUnit.SECONDS)){
                log.info("lock is ready");
                TimeUnit.SECONDS.sleep(5);
            }
        } catch (InterruptedException e) {
            log.error("obtain lock error",e);
        } finally {
            lock.unlock();
        }
    }
}
  • 测试
    启动多个实例,分别访问/lock/redis 端点,一个正常秩序业务逻辑,另外一个实例访问出现如下错误说明第二个实例没有拿到锁,证明了分布式锁的存在。

注意,如果使用新版Springboot进行集成时需要使用Redis4版本,否则会出现下面的异常告警,主要是 unlock() 释放锁时使用了UNLINK命令,这个需要Redis4版本才能支持。

2020-05-14 11:30:24,781 WARN  RedisLockRegistry:339 - The UNLINK command has failed (not supported on the Redis server?); falling back to the regular DELETE command
org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'UNLINK'


基于Zookeeper实现

  • 引入组件
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
 <dependency>
 <groupId>org.springframework.integration</groupId>
 <artifactId>spring-integration-zookeeper</artifactId>
</dependency>
  • 在application.yml中添加zookeeper的配置
zookeeper:  
    host: 172.31.0.43:2181
  • 建立配置类,注入ZookeeperLockRegistry
@Configuration
public class ZookeeperLockConfiguration {
    @Value("${zookeeper.host}")
    private String zkUrl;
    @Bean
    public CuratorFrameworkFactoryBean curatorFrameworkFactoryBean(){
        return new CuratorFrameworkFactoryBean(zkUrl);
    }
    @Bean
    public ZookeeperLockRegistry zookeeperLockRegistry(CuratorFramework curatorFramework){
        return new ZookeeperLockRegistry(curatorFramework,"/zookeeper-lock");
    }
}
  • 编写测试代码
@RestController
@RequestMapping("lock")
@Log4j2
public class DistributedLockController {
    @Autowired
    private ZookeeperLockRegistry zookeeperLockRegistry;
    @GetMapping("/zookeeper")
    public void test2() {
        Lock lock = zookeeperLockRegistry.obtain("zookeeper");
        try{
            //尝试在指定时间内加锁,如果已经有其他锁锁住,获取当前线程不能加锁,则返回false,加锁失败;加锁成功则返回true
            if(lock.tryLock(3, TimeUnit.SECONDS)){
                log.info("lock is ready");
                TimeUnit.SECONDS.sleep(5);
            }
        } catch (InterruptedException e) {
            log.error("obtain lock error",e);
        } finally {
            lock.unlock();
        }
    }
}
  • 测试
    启动多个实例,分别访问/lock/zookeeper 端点,一个正常秩序业务逻辑,另外一个实例访问出现如下错误说明第二个实例没有拿到锁,证明了分布式锁的存在。
目录
相关文章
|
iOS开发 Python Windows
|
11月前
|
人工智能 算法 JavaScript
DeepSeek横空出世,真的拯救了算力焦虑吗?
DeepSeek的突破无疑将竞争维度从“硬碰硬”的算力堆砌转向“巧实力”的算法创新,为后发者开辟了新赛道。然而,硅谷巨头的天价投资和持续膨胀的算力需求表明:算法优化是“节流”,而算力基建才是“开源”。在可见的未来,两者的螺旋式博弈仍将定义AI时代的权力格局。 或许,真正的赢家将是那些“左手握算法利刃,右手筑算力高墙”的玩家——毕竟,汽油和发动机,从不是非此即彼的选择。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
Java
SpringBoot 内部方法调用,事务不起作用的原因及解决办法
在做业务开发时,遇到了一个事务不起作用的问题。大概流程是这样的,方法内部的定时任务调用了一个带事务的方法,失败后事务没有回滚。查阅资料后,问题得到解决,记录下来分享给大家。
769 4
STM32:GPIO控制LED闪烁代码部分(内含配置图+代码+代码注释)
STM32:GPIO控制LED闪烁代码部分(内含配置图+代码+代码注释)
1123 0
STM32:GPIO控制LED闪烁代码部分(内含配置图+代码+代码注释)
|
缓存 安全 Java
Java并发编程中的线程安全问题及解决方法
在Java编程中,线程安全是一个至关重要的问题,特别是在并发编程中。本文将探讨Java并发编程中常见的线程安全问题,包括数据竞争、死锁和内存可见性,并介绍了相应的解决方法,如使用同步锁、并发容器和原子类等技术,以确保多线程环境下程序的正确性和性能。
262 29
|
机器学习/深度学习 人工智能 安全
AI时代:程序员如何重塑核心竞争力
【8月更文第5天】近年来,人工智能(AI)和生成式预训练模型(AIGC)的飞速发展对软件开发行业产生了深远的影响。ChatGPT、Midjourney、Claude 等大语言模型的出现,不仅极大地提高了编程效率,还改变了程序员的工作方式。随着AI辅助编程工具的日益普及,程序员们面临着前所未有的机遇与挑战。本文旨在探讨在AI时代,程序员应如何调整自己的职业路径和发展策略,以保持和提升自身的竞争力。
1285 0
|
消息中间件 数据采集 关系型数据库
离线数仓(三)【业务日志采集平台搭建】(2)
离线数仓(三)【业务日志采集平台搭建】
|
API 数据安全/隐私保护
短信服务的认证机制有哪些
短信服务的认证机制有哪些
|
机器学习/深度学习 传感器 算法
【红绿灯检测】基于matlab实现交通灯颜色检测
【红绿灯检测】基于matlab实现交通灯颜色检测