Springboot(十三)分布式锁redisson

前言:

      分布式锁一般有三种实现方式:

        1.基于数据库的乐观锁

        2.基于redis的分布式锁

        3.基于zookeepr的分布式锁

    本文介绍第二种,基于redis的分布式锁,官方推荐用redisson,redisson支持四种连接方式,文中写了两种一个是单体,一个是集群

Cluster集群,Sentinel哨兵,Master/Slave主从,Single单体


代码:自己搭建一个springboot工程

   代码中有详细注释,就不介绍具体方法了


首先写一个属性配置类,用来注入redis锁的一些配置信息:

package com.xhx.springboot.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * xuhaixing
 * 2018/7/2 18:16
 **/

@ConfigurationProperties(prefix ="redisson")
public class RedissonProperties {
    private String address;
    private String password;
    //毫秒
    private int timeout=30000;
    private int database=0;
    private String sentinelAddress;
    private int connectionPoolSize=150;
    private int connectionMiniumIdleSize=10;
    private int slaveConnectionPoolSize=300;
    private int masterConnectionPoolSize=300;
    private String[] sentinelAddresses;
    private String[] masterAddresses;
    //毫秒
    private int scanInterval=2000;
    private String masterName;

    public String[] getMasterAddresses() {
        return masterAddresses;
    }

    public void setMasterAddresses(String[] masterAddresses) {
        this.masterAddresses = masterAddresses;
    }

    public int getScanInterval() {
        return scanInterval;
    }

    public void setScanInterval(int scanInterval) {
        this.scanInterval = scanInterval;
    }

    public int getMasterConnectionPoolSize() {
        return masterConnectionPoolSize;
    }

    public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
        this.masterConnectionPoolSize = masterConnectionPoolSize;
    }

    public String getMasterName() {
        return masterName;
    }

    public void setMasterName(String masterName) {
        this.masterName = masterName;
    }

    public int getConnectionPoolSize() {
        return connectionPoolSize;
    }

    public void setConnectionPoolSize(int connectionPoolSize) {
        this.connectionPoolSize = connectionPoolSize;
    }

    public int getConnectionMiniumIdleSize() {
        return connectionMiniumIdleSize;
    }

    public void setConnectionMiniumIdleSize(int connectionMiniumIdleSize) {
        this.connectionMiniumIdleSize = connectionMiniumIdleSize;
    }

    public int getSlaveConnectionPoolSize() {
        return slaveConnectionPoolSize;
    }

    public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
        this.slaveConnectionPoolSize = slaveConnectionPoolSize;
    }

    public String[] getSentinelAddresses() {
        return sentinelAddresses;
    }

    public void setSentinelAddresses(String[] sentinelAddresses) {
        this.sentinelAddresses = sentinelAddresses;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public int getDatabase() {
        return database;
    }

    public void setDatabase(int database) {
        this.database = database;
    }

    public String getSentinelAddress() {
        return sentinelAddress;
    }

    public void setSentinelAddress(String sentinelAddress) {
        this.sentinelAddress = sentinelAddress;
    }
}

写一个redisson.properties配置文件

redisson.address=redis://192.168.94.151:6379
redisson.password=xuhaixing
redisson.timeout=3000
redisson.database=0

maven中引入redisson的包

<?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>com.xhx.springboot</groupId>
    <artifactId>distribute-lock</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>distribute-lock</name>
    <description>Demo project for Spring Boot</description>

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

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

    <dependencies>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.5</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

    </dependencies>

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


</project>

配置类,注入RedissonClient,程序中用RedissonClient操作锁

package com.xhx.springboot.config;

import com.xhx.springboot.properties.RedissonProperties;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * xuhaixing
 * 2018/7/2 18:25
 **/
@Configuration
@AutoConfigureAfter(value = {RedissonProperties.class})
public class RedissonAutoConfiguration {

    @Autowired
    private RedissonProperties redissonProperties;


    /**
     * 单体的
     * @return
     */
    @Bean
    @ConditionalOnProperty(value = "redisson.address")
    public RedissonClient redissonSingle(){
        Config config = new Config();
        SingleServerConfig serverConfig = config.useSingleServer().setAddress(redissonProperties.getAddress())
                .setTimeout(redissonProperties.getTimeout())
                .setDatabase(redissonProperties.getDatabase())
                .setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
                .setConnectionMinimumIdleSize(redissonProperties.getConnectionMiniumIdleSize());
        if(StringUtils.isNotEmpty(redissonProperties.getPassword())){
            serverConfig.setPassword(redissonProperties.getPassword());
        }
        return Redisson.create(config);
    }

    /**
     * 集群的
     * @return
     */
    @Bean
    @ConditionalOnProperty(value = "redisson.masterAddresses")
    public RedissonClient redissonSentinel(){
        Config config = new Config();
        ClusterServersConfig serverConfig = config.useClusterServers().addNodeAddress(redissonProperties.getMasterAddresses())
                .setTimeout(redissonProperties.getTimeout())
                //设置集群扫描时间
                .setScanInterval(redissonProperties.getScanInterval())
                //主节点线程池数量
                .setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
                //从节点线程池数量
                .setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());

        if(StringUtils.isNotEmpty(redissonProperties.getPassword())){
            serverConfig.setPassword(redissonProperties.getPassword());
        }
        return Redisson.create(config);
    }
}

封装了一个分布式锁的父类:

package com.xhx.springboot.lock;

import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

/**
 * xuhaixing
 * 2018/7/2 17:56
 **/
public interface DistributedLocker {
    RLock lock(String lockKey);
    RLock lock(String lockKey,int timeout);
    RLock lock(String lockKey, TimeUnit unit,int timeout);
    boolean tryLock(String lockKey,TimeUnit unit,int waitTime,int leaseTime);
    void unlock(String lockKey);
    void unlock(RLock lock);
}

子类:

package com.xhx.springboot.lock;

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;

/**
 * xuhaixing
 * 2018/7/2 18:03
 **/
@Component
public class RedissonDistributedLocker implements DistributedLocker {

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 没有超时时间,默认30s
     * @param lockKey
     * @return
     */
    @Override
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    /**
     * 自己设置超时时间
     * @param lockKey 锁的key
     * @param timeout  秒  如果是-1,直到自己解锁,否则不会自动解锁
     * @return
     */
    @Override
    public RLock lock(String lockKey, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
    }

    @Override
    public RLock lock(String lockKey, TimeUnit unit, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout,unit);
        return lock;
    }

    /**
     *
     * @param lockKey  锁key
     * @param unit  锁单位
     * @param waitTime   等到最大时间,强制获取锁
     * @param leaseTime  锁失效时间
     * @return
     */
    @Override
    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime,leaseTime,unit);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    @Override
    public void unlock(RLock lock) {
        lock.unlock();
    }

}


启动类:

package com.xhx.springboot;

import com.xhx.springboot.properties.RedissonProperties;
import org.redisson.api.RedissonClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication
@PropertySource(value = {"classpath:redisson.properties"})
@EnableConfigurationProperties(value = {RedissonProperties.class})
public class DistributeLockApplication {

    public static void main(String[] args) {
        SpringApplication.run(DistributeLockApplication.class, args);
    }
}

测试类:

package com.xhx.springboot;

import com.xhx.springboot.lock.RedissonDistributedLocker;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class DistributeLockApplicationTests {

    @Autowired
    private RedissonDistributedLocker redissonLocker;

    @Test
    public void contextLoads() throws Exception {
        int count = 10;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
            Thread thread = new Thread(() -> {
                try {
                    String lockKey = "17631701110";
                    redissonLocker.tryLock(lockKey, TimeUnit.SECONDS, 100, 8);
                    System.out.println("===加锁===" + Thread.currentThread().getName());

                    System.out.println("===做自己操作===");
                    Thread.sleep(5000);

                    System.out.println("===释放锁===" + Thread.currentThread().getName());
                    redissonLocker.unlock(lockKey);

                    System.out.println(latch.getCount());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                latch.countDown();
            });
            thread.start();

        }
        latch.await();
    }

}


程序执行结果:


我的github代码地址


猜你喜欢

转载自blog.csdn.net/u012326462/article/details/80891278