Spring自带分布式锁你用过吗?

环境:SpringBoot2.7.12

本篇文章将会为大家介绍有关spring integration提供的分布式锁功能。

1. 简介

Spring Integration 是一个框架,用于构建事件驱动的应用程序。在 Spring Integration 中,LockRegistry 是一个接口,用于管理分布式锁。分布式锁是一种同步机制,用于确保在分布式系统中的多个节点之间对共享资源的互斥访问。

LockRegistry及相关子接口(如:RenewableLockRegistry) 接口的主要功能:

  • 获取锁:当应用程序需要访问共享资源时,它可以通过 LockRegistry 获取一个锁。
  • 释放锁:当应用程序完成对共享资源的访问后,它应该释放锁,以便其他应用程序可以获取它(第一点中提到,并没有提供直接释放锁的操作,而是内部自动完成)。
  • 续期:提供续期机制,以便在需要时延长锁的持有时间。

常见的 LockRegistry 实现包括基于数据库、ZooKeeper 和 Redis 的实现。

公共依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-integration</artifactId>
</dependency>

2. 基于数据库分布式锁

引入依赖

<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.30</version>
</dependency>

配置

spring:
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring_lock?serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&useSSL=false
    username: root
    password: xxxooo
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimumIdle: 10
      maximumPoolSize: 200
---
spring:
  integration:
    jdbc:
      initialize-schema: always
      # 基于数据库需要执行初始化脚本
      schema: classpath:schema-mysql.sql

注册核心Bean对象

@Bean
public DefaultLockRepository defaultLockRepository(DataSource dataSource) {
  DefaultLockRepository lockRepository = new DefaultLockRepository(dataSource);
  // 这里根据你的业务需要,配置表前缀,默认:IN_
  lockRepository.setPrefix("T_") ;
  return lockRepository ;
}


// 注册基于数据库的分布式锁
@Bean
public JdbcLockRegistry jdbcLockRegistry(DefaultLockRepository lockRepository) {
  return new JdbcLockRegistry(lockRepository) ;
}

测试用例

@Test
public void testLock() throws Exception
  int len = 10 ;
  CountDownLatch cdl = new CountDownLatch(len) ;
  CountDownLatch waiter = new CountDownLatch(len) ;
  Thread[] ts = new Thread[len] ;
  for (int i = 0; i < len; i++) {
    ts[i] = new Thread(() -> {
      waiter.countDown() ;
      System.out.println(Thread.currentThread().getName() + " - 准备获取锁") ;
      try {
        waiter.await() ;
      } catch (InterruptedException e1) {
        e1.printStackTrace();
      }
      // 获取锁
      Lock lock = registry.obtain("drug_store_key_001") ;
      lock.lock() ;
      System.out.println(Thread.currentThread().getName() + " - 获取锁成功") ;
      try {
        try {
          TimeUnit.SECONDS.sleep(2) ;
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      } finally {
        // 释放锁
        lock.unlock() ;
        cdl.countDown() ;
        System.out.println(Thread.currentThread().getName() + " - 锁释放成功") ;
      }
    }, "T - " + i) ;
  }
  for (int i = 0; i < len; i++) {
    ts[i].start() ; 
  }
  cdl.await() ;
}

数据库

图片

锁的实现JdbcLock,该对象实现了java.util.concurrent.locks.Lock,所以该锁是支持重入等操作的。

配置锁获取失败后的重试间隔,默认值100ms

JdbcLockRegistry jdbcLockRegistry = new JdbcLockRegistry(lockRepository);
// 定义锁对象时设置当获取锁失败后重试间隔时间。
jdbcLockRegistry.setIdleBetweenTries(Duration.ofMillis(200)) ;

锁续期

jdbcLockRegistry.renewLock("drug_store_key_001");

3. 基于Redis分布式锁

引入依赖

<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

spring:
  redis:
    host: localhost
    port: 6379
    password: xxxooo
    database: 8
    lettuce:
      pool:
        maxActive: 8
        maxIdle: 100
        minIdle: 10
        maxWait: -1

测试用例

测试代码与上面基于JDBC的一样,只需要修改调用加锁的代码即可

Lock lock = redisLockRegistry.obtain("001") ;

设置锁的有效期,默认是60s

// 第三个参数设置了key的有效期,这里改成10s
RedisLockRegistry redisLockRegistry = new RedisLockRegistry(connectionFactory, registryKey, 10000) ;

注意:redis key的有效期设置为10s,如果你的业务执行超过了10s,那么程序将会报错。并没有redission watch dog机制。

Exception in thread "T - 0" java.lang.IllegalStateException: Lock was released in the store due to expiration. The integrity of data protected by this lock may have been compromised.
  at org.springframework.integration.redis.util.RedisLockRegistry$RedisLock.unlock(RedisLockRegistry.java:450)
  at com.pack.SpringIntegrationDemoApplicationTests.lambda$1(SpringIntegrationDemoApplicationTests.java:83)
  at java.lang.Thread.run(Thread.java:748)

如果10s过期后key自动删除后,其它线程是否能立马获取到锁呢?如果是单节点中其它现在也不能获取锁,必须等上一个线程结束后才可以,这是因为在内部还维护了一个ReentrantLock锁,在获取分布式锁前要先获取本地的一个锁。

private abstract class RedisLock implements Lock {
  private final ReentrantLock localLock = new ReentrantLock();
  public final void lock() {
      this.localLock.lock();
      while (true) {
        try {
          if (tryRedisLock(-1L)) {
            return;
          }
        } catch (InterruptedException e) {
        } catch (Exception e) {
          this.localLock.unlock();
          rethrowAsLockException(e);
        }
      }
    }
}

注意:不管是基于数据库还是Redis都要先获取本地的锁

Spring Cloud Task就使用到了Spring Integration中的锁基于数据库的。

总结:Spring Integration 的分布式锁为开发者提供了一种在分布式系统中实现可靠同步的有效方法。通过合理选择和使用这些锁实现,可以确保对共享资源的访问在多个节点之间保持协调一致,从而提高系统的整体可靠性和性能。

完毕!!!

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
产品名字 版本 centos 7.5 kubernetes 1.13 docker 18.09.5 JDK 1.8_131 SSH 7.4 etcd 3.3.10(API version:2) flannel 0.10.0 TOMCAT 8 Mysql 5.6 Ambari 2.7 HDP 3.0 ElasticSearch 6.5.1 Kibana 6.5.1 Logstash 6.5.1 Neo4j 3.4 esxi 6.5 MAVEN 3.6 ElasticSearch 6.5.1 Kibana 6.5.1 Logstash 6.5.1 目录: 1. ESXI6.5创建centos虚拟机 10 1.1. 准备工作 10 1.2. 创建新的虚拟机 10 2. ESXI虚拟机克隆 13 2.1. 创建文件夹 13 2.2. 复制文件 13 2.3. 注册现有虚拟机 14 2.4. 为克隆的虚拟机添加硬盘 15 3. HPE服务器Centos7.5安装 16 3.1. 服务器(DL388 Gen10)加载镜像文件 16 3.2. 系统安装、配置 18 3.2.1. 选中【install centos7】 18 3.2.2. 语言 18 3.2.3. 其他配置 18 3.2.4. 时区 18 3.2.5. 软件包安装 19 3.2.6. 磁盘分区 19 3.2.7. root用户密码配置 20 3.2.8. 完成安装 20 3.3. IP地址、主机名设置 20 3.4. 磁盘挂载并开机自动挂载 22 3.4.1. 磁盘挂载(如果超过2T的挂载则看超过2T的挂载章节) 22 3.4.2. 设置开机时自动挂载 23 3.4.3. 超过2T的挂载 23 3.5. 磁盘分区扩容 25 3.5.1. 根目录 25 3.5.2. 普通目录 26 3.6. Vim命令安装 26 3.7. Wget命令安装 26 3.8. 查看端口占用命令 26 3.9. 新建用户、用户组并设置root权限 26 4. SSH免密 27 4.1. 查看SSH是否安装,需要客户端与服务都安装,centos7自带ssh服务,一般可以跳过安装步骤 27 4.2. 安装命令 27 4.3. service sshd restart 启动、重启SSH服务; 27 4.4. 开机启动。 27 4.5. 退出SSH命令 27 4.6. SSH免密登录 27 5. 同步系统时间,(分布式系统各个节点需要同步时间) 27 5.1. 安装NTP插件 27 5.2. 同步时间: 27 6. JDK 28 7. Maven 28 8. Tomcat 29 8.1. 配置tomcat的环境变量和内存设置 29 8.2. 服务自启动 30 9. Mysql 30 9.1. 离线安装 30 9.2. 在线安装 30 9.2.1. 检测系统是否自带安装mysql 30 9.2.2. 下载yum包 30 9.2.3. 安装mysql 30 9.2.4. 启动服务 30 9.2.5. 设置root密码 30 9.2.6. 设置开机自启 31 9.2.7. mysql安全设置 31 9.2.8. 创建数据库 31 9.2.9. 创建用户 31 9.2.10. 更改数据存储路径 31 9.2.11. 开启binlog日志 32 9.2.12. 修改mysql配置文件 32 9.3. 问题整理 33 10. Apache Http文件服务器 33 10.1. 安装http 33 10.2. 修改Http服务器根路径 33 10.3. 中文乱码 33 10.3.1. 文件内容中文乱码 33 10.3.2. 文件名字中文乱码 33 10.4. 修改目录文件名不要被截断 33 11. Ambari 33 11.1. 安装相关服务 33 11.2. 同步所有节点时间 34 11.3. IP与主机名映射 34 11.4. SSH免密 34 11.5. JDK安装(建议1.8) 34 11.6. 关闭防火墙、selinux、THP 34 11.7. 最大打开文件要求 34 11.8. mysql配置(server机器操作) 34 11.8.1. 创建数据库 34 11.8.2. 下载mysql-connection-java 34 11.9. 部署本地库(镜像服务器)(server机器操作) 35 11.9.1. 下载HortWorks官网上的4个库到本地(也可以在线下载,但是速度会很慢) 35 11.9.2. 创建本地yum源 35 11.9.3. 上传文件 35 11.9.4. 解压文件 35 11.9.5. 新建一个httpd资源库文件夹 35 11.9.6. 修改本地源地址 35 11.10. Ambari-server端安装部署(server机器操作) 36 11.10.1. 安装Ambari-server 36 11.10.2. 配置Ambari-server 36 11.10.3. 查看并启动Ambari-server 37 11.10.4. Ambari登陆 38 11.11. Ambari集群搭建(所有agent节点机器操作) 38 11.11.1. 安装agent 38 11.11.2. 部署Agent 39 11.11.3. 创建集群: 39 11.11.4. 存储库地址配置: 40 11.11.5. 节点配置: 40 11.11.6. 节点验证: 41 11.11.7. 选择需要安装的服务: 41 11.11.8. 主服务节点配置,尽量均匀分布: 42 11.11.9. 从服务、客户端节点配置: 42 11.11.10. 密码配置: 42 11.11.11. 数据库配置 42 11.11.12. 修改服务存储、日志路径: 44 11.11.13. 用户名配置 44 11.11.14. 配置文件更改 45 11.11.15. 部署 45 11.12. Ambari 卸载服务 48 11.13. 登陆地址 48 11.14. 异常处理 48 11.15. 警告 50 11.15.1. Ambari Metrics ,Hbase rootdir路径问题 50 11.15.2. Atlas 51 12. Ambari集成ElasticSearch6.x 51 12.1. 通过SSH把源码传到centos机器上 51 12.2. 复制源码到Ambari 51 12.3. 编辑metainfo.xml文件 51 12.4. 启动/重启Ambari 51 12.5. 添加服务 51 13. ElasticSearch集群搭建 54 13.1. 解压文件:必须使用非root用户安装和启动 54 13.2. 创建目录 54 13.3. 配置文件 55 13.4. 系统配置文件 55 13.5. 启动服务 56 13.6. 添加系统服务自启动 56 13.7. Head安装 57 13.8. 登陆地址 57 13.9. ES跨域访问配置 57 13.10. IK分词器安装 57 13.11. Cerebro配置 57 13.12. 集群使用经验 58 13.13. 问题整理 58 14. Elasticsearch与reactivesearch快速构建搜索项目 58 15. kibana 59 15.1. 配置kibana配置文件 59 15.2. 启动、停止服务 59 15.3. 登陆地址 59 15.4. 汉化 59 16. Neo4j 59 16.1. 下载地址: 59 16.2. 解压缩: 59 16.3. SSH复制文件到其他节点: 60 16.4. 启动服务: 60 16.5. 登陆地址 60 16.6. 账号密码: 60 16.7. neo4服务自启动 60 16.8. 配置文件解释 61 17. 安装logstash插件同步ES-mysql数据 65 17.1. 下载logstash(版本需与ES一致) 65 17.2. 安装logstash-input-jdbc插件步骤: 65 17.3. 启动logstash 66 18. HDFS 67 18.1. 退出安全模式: 67 19. Kafka 67 19.1. 优化 67 20. Spark2.x 68 20.1. 示例执行 68 21. YARN 68 21.1. 错误处理 68 22. Docker安装 68 22.1. 在线安装 68 22.1.1. 使用国内docker源 68 22.1.2. Docker安装 68 22.1.3. 启动进程 68 22.2. RPM安装方式 68 22.2.1. 安装libtool-ltdl 68 22.2.2. 关闭selinux和内存交换 69 22.2.3. 安装docker-ce-selinux,docker-ce 69 22.3. 二进制文件安装方式 69 22.3.1. 下载地址 69 22.3.2. 安装配置 69 23. Kubernetes集群搭建准备 70 23.1. 机器规划: 70 23.2. 所有节点添加环境变量 70 23.3. 下载地址 70 23.4. 解压文件 70 23.5. Server节点CA证书创建分发 70 23.5.1. 安装cfssl 70 23.5.2. 创建用来生成 CA 文件的 JSON 配置文件 71 23.5.3. 创建用来生成 CA 证书签名请求(CSR)的 JSON 配置文件 71 23.5.4. 生成CA证书(ca.pem)和密钥(ca-key.pem) 71 23.5.5. 分发证书 72 24. ETCD数据库集群搭建 72 24.1. 创建etcd证书签名请求 72 24.2. 生成证书和私钥 73 24.3. 创建配置文件 73 24.4. 创建服务文件 74 24.5. 拷贝etcd.conf和etcd.service文件到其他2个节点 74 24.6. 启动服务 74 24.7. 测试集群 75 24.8. 注意事项 75 25. Kubernetes集群安装部署 75 25.1. Master节点部署 75 25.1.1. 复制文件 75 25.1.2. 部署kube-apiserver服务 75 25.1.3. 部署kube-controller-manager服务 78 25.1.4. 部署kube-scheduler服务 79 25.1.5. 部署kubectl 命令行工具 79 25.2. Node节点部署 81 25.2.1. 复制文件 81 25.2.2. 部署kubelet 81 25.2.3. 部署Kube-proxy服务 84 25.3. Flannel网络配置 86 25.3.1. 下载地址 86 25.3.2. 解压文件 86 25.3.3. 复制文件 86 25.3.4. 生成ca配置文件与证书 86 25.3.5. 分发证书 87 25.3.6. 配置文件 87 25.3.7. Flannel CNI集成 88 25.3.8. 启动服务 88 25.3.9. 配置Docker使用Flannel 89 25.4. coredns安装 89 25.4.1. 编辑配置文件 89 25.4.2. 创建coredns 93 25.4.3. 查看coredns服务 93 25.4.4. Pod容器中进行域名解析测试 93 25.5. kubernetes-dashboard部署 93 26. GlusterFS分布式文件系统部署 95 26.1. 在kubernetes中部署 95 26.1.1. 安装: 95 26.1.2. 创建Heketi服务: 97 26.1.3. 为Heketi设置GlusterFS集群 99 26.2. yum部署 99 26.3. 创建分布式卷 99 27. SpringBoot 100 28. SpringCloud 100

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值