sprinBoot2.x 整合 Redission (分布式锁)

【简介】

实际开发中会遇到分布式锁的情况,解决方案有数据库(不推荐)、Redis(Redission 推荐)、zookeeper等

本文主要是springBoot2.x整合Redission的demo,所以并没有整合数据库和mybatis等组件


【本文 Demo】

https://github.com/qidasheng2012/springboot2.x_redis/tree/branch-redisson


【项目结构】

sprinBoot2.x 整合 Redission (分布式锁)_第1张图片


【pom依赖】


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.0.RELEASEversion>
        <relativePath/> 
    parent>

    <groupId>com.examplegroupId>
    <artifactId>springboot2.x_redisartifactId>
    <version>1.0.0version>
    <name>springboot2.x_redisname>
    <description>SpringBoot2.x demo project for Redisdescription>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-aopartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-pool2artifactId>
        dependency>

        
        <dependency>
            <groupId>org.redissongroupId>
            <artifactId>redissonartifactId>
            <version>3.11.0version>
        dependency>

        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>3.8.1version>
        dependency>

        <dependency>
            <groupId>commons-iogroupId>
            <artifactId>commons-ioartifactId>
            <version>2.4version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>

    dependencies>

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

project>


【application.yml】

相比properties文件结构更清晰【推荐使用yml文件】

server:
  port: 80

spring:
  redis:
    #数据库索引
    database: 0
    host: 127.0.0.1
    port: 6379
    password: root
    lettuce:
      pool:
        #最大连接数
        max-active: 8
        #最大阻塞等待时间(负数表示没限制)
        max-wait: -1
        #最大空闲
        max-idle: 8
        #最小空闲
        min-idle: 0
        #连接超时时间
        timeout: 10000


【RedisProperties】

package com.example.springboot_redis.redis;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * Redis配置文件的参数
 *
 * @author qp
 * @date 2019/7/19 17:35
 */
@Data
@Component
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private String host;
    private int port;
    private String password;
    private int database;
}

【RedissionConfig】

package com.example.springboot_redis.redis;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Redission配置类
 *
 * @author qp
 * @date 2019/7/19 14:49
 */
@Slf4j
@Configuration
public class RedissionConfig {

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    public RedissonClient redissonClient() {
        RedissonClient redissonClient;

        Config config = new Config();
        String url = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort();
        config.useSingleServer().setAddress(url)
                .setPassword(redisProperties.getPassword())
                .setDatabase(redisProperties.getDatabase());

        try {
            redissonClient = Redisson.create(config);
            return redissonClient;
        } catch (Exception e) {
            log.error("RedissonClient init redis url:[{}], Exception:", url, e);
            return null;
        }
    }

}

【DistributedRedisLock】

package com.example.springboot_redis.redis;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 分布式Redis锁
 *
 * @author qp
 * @date 2019/7/19 15:21
 */
@Slf4j
@Component
public class DistributedRedisLock {

    @Autowired
    private RedissonClient redissonClient;

    // 加锁
    public Boolean lock(String lockName) {
        try {
            if (redissonClient == null) {
                log.info("DistributedRedisLock redissonClient is null");
                return false;
            }

            RLock lock = redissonClient.getLock(lockName);
            // 锁10秒后自动释放,防止死锁
            lock.lock(10, TimeUnit.SECONDS);

            log.info("Thread [{}] DistributedRedisLock lock [{}] success", Thread.currentThread().getName(), lockName);
            // 加锁成功
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
            return false;
        }
    }

    // 释放锁
    public Boolean unlock(String lockName) {
        try {
            if (redissonClient == null) {
                log.info("DistributedRedisLock redissonClient is null");
                return false;
            }

            RLock lock = redissonClient.getLock(lockName);
            lock.unlock();
            log.info("Thread [{}] DistributedRedisLock unlock [{}] success", Thread.currentThread().getName(), lockName);
            // 释放锁成功
            return true;
        } catch (Exception e) {
            log.error("DistributedRedisLock unlock [{}] Exception:", lockName, e);
            return false;
        }
    }

}

【LockTestController】

package com.example.springboot_redis.contoller;

import com.example.springboot_redis.redis.DistributedRedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 分布式Redis锁测试controller
 *
 * @author qp
 * @date 2019/7/19 17:30
 */
@RestController
@RequestMapping("/lock")
public class LockTestController {

    @Autowired
    private DistributedRedisLock distributedRedisLock;

    // 测试分布式锁
    @GetMapping("/testLock")
    public void testLock() {
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Boolean lockFlag = distributedRedisLock.lock("LOCK");
                }
            }).start();
        }
    }

}

【测试】

sprinBoot2.x 整合 Redission (分布式锁)_第2张图片

【测试结果】

sprinBoot2.x 整合 Redission (分布式锁)_第3张图片

【结果分析】

1、调用方法启用5个线程,每个线程都去获取分布式锁,只有一个线程能获取到锁,其他线程均处于阻塞状态

2、因为没有调用释放锁的方法,且在获取锁的lock()方法中设置了锁的最大时间为10秒(防止死锁的发生),所以在10秒后锁自动释放,由其他线程进行竞争这把分布式锁然后执行


【总结】

在分布式的情况下可以使用redission作为分布式锁,当然还有ZK、数据库的乐观锁(version)或悲观锁(for update)等等根据具体的业务场景进行选择

在实际使用redission作为分布式锁时,操作步骤如下:

  • 1、调用distributedRedisLock.lock(String lockName) 获取分布式锁
  • 2、如果返回true,执行业务逻辑。如果返回false,进行阻塞
  • 3、当执行完业务逻辑后,调用distributedRedisLock.unlock(String lockName)释放锁

【推荐好文】

SpringBoot + Redis分布式锁

SpringBoot + Redis分布式锁实战

你可能感兴趣的:(Redis,SpringBoot)