通过SpringBoot更优雅的实现分布式锁(Spring Integration)

一、概述

之前的文章中,有提到过用redission组件实现分布式锁,实际上除了几种经常被采用的,如:

  • 基于关系型数据库
  • 基于Redission组件
  • 基于Apache Curator组件,通过Zk的临时顺序节点模型实现的

除了以上比较常用的方式,这篇文章简单说一下感觉被大伙儿忽略的一种实现方式,那就是通过 Spring Integration 这个新兴组件来实现,实际上它的核心目标就是提供一个简单的模型来实现复杂的企业集成解决方案,为基于Spring的应用添加异步的、消息驱动的行为,让Spring用户可以直观的、增量的采用,更详细的内容可以通过官网深入了解。Spring Integration提供的全局锁目前为如下存储提供了实现:

  • Redis
  • Zookeeper
  • Gemfire
  • JDBC

它们使用相同的API抽象,这意味着,不论使用哪种存储,你的编码体验都是一样的。试想一下你当前项目是基于zookeeper实现的分布式锁,后续由于特殊需求变化,你想换成redis的实现,我们只需要修改相关依赖和配置就可以了,无需修改代码。

二、API说明

在使用 Spring Integration 实现分布式锁之前,我们需要重点关注几个方法:
lock():加锁,如果已经被其他线程锁住或者当前线程不能获取锁则阻塞;
lockInterruptibly():加锁,除非当前线程被打断;
tryLock():尝试加锁,如果已经有其他锁锁住,获取当前线程不能加锁,则返回false,加锁失败;加锁成功则返回true;
tryLock(long time, TimeUnit unit):尝试在指定时间内加锁,如果已经有其他锁锁住,获取当前线程不能加锁,则返回false,加锁失败;加锁成功则返回true;
unlock():R解锁。

三、案例

采用Redis和Zookeeper可能大家更亲切,就以这俩为例,JDBC和Gemfire可以自己玩玩,Gemfire其实我也不会,嗯:

  • 依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-integration</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-zookeeper</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>
</dependencies>
  • 配置
# 端口随意改几个,分别启动,模拟多个实例
server.port=12010
spring.redis.host=192.168.56.10
  • Redis锁配置类
package com.ideax.lock.plus.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.integration.redis.util.RedisLockRegistry;

/**
 * Redis锁配置类
 *
 * @author zhangxs
 **/
@Configuration
public class RedisLockConfiguration {
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        return new RedisLockRegistry(redisConnectionFactory, "redis-lock");
    }
}
  • Zookeeper锁配置类
package com.ideax.lock.plus.config;

import org.apache.curator.framework.CuratorFramework;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.zookeeper.config.CuratorFrameworkFactoryBean;
import org.springframework.integration.zookeeper.lock.ZookeeperLockRegistry;

/**
 * Zk锁配置类
 *
 * @author zhangxs
 **/
@Configuration
public class ZkLockConfiguration {
    @Bean
    public CuratorFrameworkFactoryBean curatorFrameworkFactoryBean() {
        return new CuratorFrameworkFactoryBean("10.18.28.115:2181");
    }

    @Bean
    public ZookeeperLockRegistry zookeeperLockRegistry(CuratorFramework curatorFramework) {
        return new ZookeeperLockRegistry(curatorFramework, "/zookeeper-lock");
    }
}
  • 测试代码
package com.ideax.lock.plus.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.integration.zookeeper.lock.ZookeeperLockRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 锁测试接口
 *
 * @author zhangxs
 **/
@RestController
@RequestMapping("lock")
public class LockController {
    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private ZookeeperLockRegistry zookeeperLockRegistry;

    @GetMapping("redis")
    public void redisDual() {
        businessMethod(redisLockRegistry.obtain("redis"));
    }

    @GetMapping("zk")
    public void zkDual() {
        businessMethod(zookeeperLockRegistry.obtain("zookeeper"));
    }

    protected void businessMethod(Lock lock) {
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                System.out.println("锁对象就绪...");
                TimeUnit.SECONDS.sleep(5);
            }
        } catch (InterruptedException ie) {
            System.out.println("锁获取失败...");
            ie.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
  • 启动多个实例,发请求看测试结果

1、短时间内对12008和12009端口下redis接口发出请求:
在这里插入图片描述
在这里插入图片描述
2、…12008/lock/redis的请求先拿到锁:
在这里插入图片描述
3、…12009/lock/redis的请求获取锁失败:
在这里插入图片描述
由此可证明,Redis分布式锁生效。

4、短时间内对12008和12009端口下zk接口发出请求:
在这里插入图片描述
在这里插入图片描述
5、…12009/lock/zk的请求先拿到锁:
在这里插入图片描述
6、…12010/lock/zk的请求获取锁失败:
在这里插入图片描述
由此可证明,Zookeeper分布式锁生效。

四、总结

个人非常推荐这种实现方式,更加灵活。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>