spring data redis setnx 分布式锁

oyhk 学习笔记


spring data redis + redis 实现程序的分布式锁,众所周知 redis 是单进程,redis 操作都是原子性,redis 是天生的线程安全。
下面来基于spring boot2 中 spring data redis 实现分布式锁。

redis setnx 命令

SETNX key value
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
可用版本:
>= 1.0.0
时间复杂度:
O(1)
返回值:
设置成功,返回 1 。
设置失败,返回 0 。

spring data redis 实现分布式锁

pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- spring boot 2 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
</project>

BootstrapSpringDataRedis.java

package com.mkfree.sample.springdataredis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class BootstrapSpringDataRedis {

    private static Logger log = LoggerFactory.getLogger(BootstrapSpringDataRedis.class);

    public static void main(String[] args) {
        ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(BootstrapSpringDataRedis.class, args);
        String[] strings = configurableApplicationContext.getBeanDefinitionNames();
    }

}

RedisLock.java

package com.mkfree.sample.springdataredis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisLock {

    private static final String LOCK_SUFFIX = "_lock";

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 分布式锁
     *
     * @param key
     * @return
     */
    public void lock(String key) {
        boolean lock;
        while (true) {
            lock = stringRedisTemplate.opsForValue().setIfAbsent(key + LOCK_SUFFIX, "");
            if (lock) {
                // 设置分布式锁最长时间为5秒,超时自动去除,防止死锁的情况发生
                stringRedisTemplate.expire(key + LOCK_SUFFIX, 10, TimeUnit.SECONDS);
                log.info("setting expire 10 seconds");
                break;
            }
        }
    }

    /**
     * 解除分布式锁
     *
     * @param key
     */
    public void unLock(String key) {
        stringRedisTemplate.delete(key + LOCK_SUFFIX);
    }
}

RedisController.java

package com.mkfree.sample.springdataredis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class RedisLockController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RedisLock redisLock;


    @GetMapping("/test1")
    public String test1() throws InterruptedException {
        try {
            // 获取当前 key 分布式锁,获取成功可以继续往下执行
            redisLock.lock("DistributedLock");

            logger.info("具体的业务代码");
            // 运行业务代码模式时间,10秒
            Thread.sleep(10000);

            return "test1";
        } finally {
            // 解锁
            redisLock.unLock("DistributedLock");
        }
    }

    @GetMapping("/test2")
    public String test2() {
        try {
            redisLock.lock("DistributedLock");
            logger.info("test2222222");
            return "test2";
        } finally {
            redisLock.unLock("DistributedLock");
        }
    }
}

验证

启动程序
1. 首先调用

发表评论