关于redis:来吧展示Redis的分布式锁及其实现Redisson的全过程

前言

分布式锁是管制分布式系统之间同步访问共享资源的一种形式。
在分布式系统中,经常须要协调他们的动作。如果不同的零碎或是同一个零碎的不同主机之间共享了一个或一组资源,那么拜访这些资源的时候,往往须要互斥来避免彼此烦扰来保障一致性,这个时候,便须要应用到分布式锁。

什么是分布式锁

1.在分布式环境中应用到的锁就是分布式锁
2.在分布式环境中对不同应用程序操作的共享资源进行加锁就是分布式锁

分布式环境

1.同一个利用下的子利用采纳独自部署的形式运行就是分布式
2.只有利用存在跨JVM就是分布式环境

为什么要有分布式锁

JDK中原生锁(Synchronized、Lock)只针对是同一个JVM实例上操作资源而言,对于不同JVM的操作是没方法进行加锁的。

分布式锁的利用场景

只有存在跨JVM操作,并且存在共享资源竞争问题,就是必须应用到分布式锁的场景。

分布式锁有哪些

常见的分布式锁有以下几种:
1.基于数据库(乐观锁)实现分布式锁
2.基于Redis的分布式锁Redisson
3.基于Zookeeper实现分布式锁

基于redis分布式锁原理

获取锁

通过Redis创立一个惟一的key,如果以后线程能创立这个惟一的key,则示意以后线程获取到锁。

开释锁

当删除Redis中的代表锁的惟一key,则示意开释锁。

什么是死锁

在开释锁时出现异常,Redis中代表锁的惟一key未被删除,而其余线程始终在自旋期待并心愿可能获取锁,但事实上所有线程都没能获取到锁的状况称为死锁。

如何解决死锁

咱们能够通过对Redis中代表锁的惟一Key设置过期工夫来防止死锁的产生。

如何防止锁被其余线程开释

创立锁时记录线程ID,本人的锁只能本人开释。

如何保障锁的可重入性

当线程获取到锁后在Redis中把以后线程的ID做为key的值进行存储,加锁时判断以后线程与Redis锁的值是否统一。

基于redis分布式锁Redisson

配置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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>site.yangpan</groupId>
        <artifactId>yangpan-spring-boot</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <groupId>site.yangpan.redission</groupId>
    <artifactId>yangpan-spring-boot-redission</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>yangpan-spring-boot-redission</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-web</artifactId>
        </dependency>

        <!--上面这两个依赖就是集成redission的依赖-->
        <!--第一个依赖与spring boot版本有关系,参考官网文档-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-data-21</artifactId>
            <version>3.13.3</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.3</version>
        </dependency>
    </dependencies>

</project>

留神:网上很多都是间接引入redission依赖,然而我这里是通过Spring Boot Starter的形式引入

配置application.properties

# 公共spring boot配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
#spring.redis.cluster.nodes=
#spring.redis.sentinel.master=
#spring.redis.sentinel.nodes=

## Redisson 配置
spring.redis.redisson.config=classpath:redisson.yaml

配置redisson.yaml

singleServerConfig:
  idleConnectionTimeout: 10000
  pingTimeout: 1000
#  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
#  reconnectionTimeout: 3000
#  failedAttempts: 3
  password:
  subscriptionsPerConnection: 5
  clientName:
  address: "redis://127.0.0.1:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 32
  connectionPoolSize: 64
  database: 0
#  dnsMonitoring: false
  dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#"transportMode":"NIO"

编写RedissonSpringDataConfig

接下来咱们注册 RedissonConnectionFactory 到 Spring context

package site.yangpan.redission.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

import java.io.IOException;

/**
 * 注册 RedissonConnectionFactory 到 Spring context
 * https://github.com/redisson/redisson/tree/master/redisson-spring-data#spring-data-redis-integration
 * Created by yangpan on 2020-08-29 19:15.
 */
@Configuration
public class RedissonSpringDataConfig {

    @Bean
    public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
        return new RedissonConnectionFactory(redisson);
    }

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException {
        Config config = Config.fromYAML(configFile.getInputStream());
        return Redisson.create(config);
    }

}

应用Redisson

这里咱们间接编写一个controller,而后应用Redisson

package site.yangpan.redission.reentrantLock;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 基于Redis的Redisson分布式可重入锁RLock
 * Java对象实现了java.util.concurrent.locks.Lock接口。
 * 同时还提供了异步(Async)、反射式(Reactive)和RxJava2规范的接口。
 * Created by yangpan on 2020-08-29 19:40.
 */
@Controller
@RestController
@RequestMapping("/redissionReentrantLock")
public class RedissonReentrantLock {

    private Integer stock = 100;

    @Autowired
    private RedissonClient redissonClient;

    @GetMapping("/test")
    public void test(){
        //应用线程池模仿并发,看分布式锁有没有问题
        ExecutorService executorService = Executors.newFixedThreadPool(8);
        for(int i=0;i<=1000;i++){
            executorService.execute(() -> {
                try {
                    Thread.sleep(3000);
                    //调用加锁办法
                    reduceStockRessionLock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    /**
     * 加锁状况
     */
    private void reduceStockRessionLock() {
        //获取锁(可重入锁)
        RLock lock = redissonClient.getLock("anyLock");

        //加锁
        lock.lock();

        //业务操作
        if(stock > 0){
            stock--;
            System.out.println("以后库存残余:" + stock);
        }

        //开释锁
        lock.unlock();
    }

    /**
     * 不加锁状况
     */
    private void reduceStock() throws InterruptedException {
        //业务操作
        if(stock > 0){
            stock--;
            System.out.println("以后库存残余:" + stock);
        }
    }

}

启动测试

接下来咱们启动我的项目,拜访接口,查看日志,察看日志剖析可得Redisson分布式锁的确起到了作用

curl http://localhost:8080/redissionReentrantLock/test

最初

感激你看到这里,看完有什么的不懂的能够在评论区问我,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享java相干技术文章或行业资讯,欢送大家关注和转发文章!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理