Spring早就提供了分布式锁的实现。原来分布式锁的相关代码存在于Spring Cloud的子项目Spring Cloud Cluster中,后来被迁到Spring Integration中。

Spring Integration为企业集成模式的实现;通俗地说,Spring Integration的定位是一个轻量级的ESB,尽管它做了很多ESB不做的事情。

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

  • Gemfire

  • JDBC

  • Redis

  • Zookeeper

这些存储都使用相同的API抽象,因此无论使用哪种存储,你的编码过程是相同的,如想更换存储实现不需要更改代码,只需要修改依赖和配置即可。

新建项目结构如下:

  1. 引入pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.11</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring_integration</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring_integration</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.修改配置文件增加redis配置

server:
  port: 8081
spring:
  redis:
    port: 6379
    host: localhost

3.添加配置类

package com.mfc.springintegration.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;

@Configuration
public class RedisLockConfiguration {
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        return new RedisLockRegistry(redisConnectionFactory, "svc-redis-lock");
    }
}

4.增加测试Controller
package com.mfc.springintegration.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
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;

@RestController
@RequestMapping(value = "redisLock")
@Slf4j
public class RedisLockController {

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @GetMapping("/t1")
    public void t1() throws InterruptedException {
        Lock lock = redisLockRegistry.obtain("lock");
        boolean l1 = lock.tryLock(10, TimeUnit.SECONDS);
        log.info("t1-l1 = {}", l1);

        TimeUnit.SECONDS.sleep(15);

        boolean l2 = lock.tryLock(10, TimeUnit.SECONDS);
        log.info("t1-l2 = {}", l2);

        lock.unlock();
        lock.unlock();
    }

    @GetMapping("/t2")
    public void t2() throws InterruptedException {
        Lock lock = redisLockRegistry.obtain("lock");
        boolean l1 = lock.tryLock(10, TimeUnit.SECONDS);
        log.info("t2-l1 = {}", l1);

        TimeUnit.SECONDS.sleep(15);

        boolean l2 = lock.tryLock(10, TimeUnit.SECONDS);
        log.info("t2-l2 = {}", l2);

        lock.unlock();
        lock.unlock();
    }
}

5.测试
t1-l1和t1-l2都是true,说明同一个线程是可以获得到锁的。因为/redisLock/t1接口线程获取了锁并且未释放所以t2-l1为false。t2-l2为true是因为接口/redisLock/t1的线程已释放了锁。
访问地址:
http://localhost:8081/redisLock/t1
http://localhost:8081/redisLock/t2