基于Redis 的setnx 实现分布式锁 和 Redisson实现分布式锁的三种方式以及实现原理

redis实现分布式锁的原理

1、利用NX的原子性,多个线程并发时,只有一个线程可以设置成功。

2、设置成功即获得锁,可以继续执行业务。

3、如果出现异常,过了锁的有效期,锁自动释放。

 

加锁:

ET resource_name my_random_value NX PX 30000 :设置分布式锁。
resource_name :为锁的key。
my_random_value : 必须保证每一个线程随机值不同、用于释放锁的时候校验(使用UUID) 。
NX :key不存在时设置成功,存在时设置不成功 。
PX :自动失效时间,锁可以过期失效防止异常情况。

 

解锁:

释放锁采用redis的delete命令。
释放锁时校验之前设置的随机数,相同才能释放。
释放锁的LUA脚本。

 

Redis分布式锁的原理图

基于Redis 的setnx 实现分布式锁 和 Redisson实现分布式锁的三种方式以及实现原理_第1张图片

SETNX实现分布式锁

pom.xm文件中引入如下依赖:

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

application.yml配置

spring:
  redis:
    host: 192.168.0.218
    port: 6379
    password: 123456

实现setnx锁的方法 (AutoCloseable 实现自动释放锁的接口

package com.example.distributelock.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Slf4j
public class RedisLock implements AutoCloseable {

    private RedisTemplate redisTemplate;
    private String key;
    private String value;
    //单位:秒
    private int expireTime;

    public RedisLock(RedisTemplate redisTemplate,String key,int expireTime){
        this.redisTemplate = redisTemplate;
        this.key = key;
        this.expireTime=expireTime;
        this.value = UUID.randomUUID().toString();
    }

    /**
     * 获取分布式锁
     * @return
     */
    public boolean getLock(){
        RedisCallback redisCallback = connection -> {
            //设置NX
            RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
            //设置过期时间
            Expiration expiration = Expiration.seconds(expireTime);
            //序列化key
            byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
            //序列化value
            byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
            //执行setnx操作
            Boolean result = connection.set(redisKey, redisValue, expiration, setOption);
            return result;
        };

        //获取分布式锁
        Boolean lock = (Boolean)redisTemplate.execute(redisCallback);
        return lock;
    }

    public boolean unLock() {
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        RedisScript redisScript = RedisScript.of(script,Boolean.class);
        List keys = Arrays.asList(key);

        Boolean result = (Boolean)redisTemplate.execute(redisScript, keys, value);
        log.info("释放锁的结果:"+result);
        return result;
    }


    @Override
    public void close() throws Exception {
        unLock();
    }
}

controller实现类

package com.example.distributelock.controller;

import com.example.distributelock.lock.RedisLock;
import com.example.distributelock.lock.ZkLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class RedisLockController {
    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("redisLock")
    public String redisLock() {
        log.info("我进入了方法!");
        try (RedisLock redisLock = new RedisLock(redisTemplate, "redisKey", 30)) {
            if (redisLock.getLock()) {
                log.info("我进入了锁!!");
                Thread.sleep(15000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }

  
}

 

 

Redisson实现分布式锁第一种实现方式(spring.xml)

 

pom.xml引入如下依赖

        
            org.redisson
            redisson
            3.11.2
        

 

创建redisson.xml 文件



    
        
    

启动类上需要导入配置文件

@SpringBootApplication
@ImportResource("classpath*:redisson.xml")
public class RedissonLockApplication {

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

}

controller实现

package com.hy.redissonlock.controller;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class RedissonLockController {
    @Resource
    private RedissonClient redisson;

    @RequestMapping("redissonLock")
    public String redissonLock() {
        RLock rLock = redisson.getLock("order");
        log.info("我进入了方法!!");
        try {
            rLock.lock(30, TimeUnit.SECONDS);
            log.info("我获得了锁!!!");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("我释放了锁!!");
            rLock.unlock();
        }
        log.info("方法执行完成!!");
        return "方法执行完成!!";
    }
}

 

启动项目测试:

基于Redis 的setnx 实现分布式锁 和 Redisson实现分布式锁的三种方式以及实现原理_第2张图片

后台日志:

基于Redis 的setnx 实现分布式锁 和 Redisson实现分布式锁的三种方式以及实现原理_第3张图片

 

Redisson实现分布式锁第二实现方式(Java API)

pom.xml引入如下依赖

        
            org.redisson
            redisson
            3.11.2
        

测试用例:

package com.hy.redissonlock;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class RedissonLockApplicationTests {

    @Test
    public void contextLoads() {
    }

    @Test
    public void testRedissonLock() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.0.218:6379");
        config.useSingleServer().setPassword("123456");

        RedissonClient redisson = Redisson.create(config);

        RLock rLock = redisson.getLock("order");

        try {
            rLock.lock(30, TimeUnit.SECONDS);
            log.info("我获得了锁!!!");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("我释放了锁!!");
            rLock.unlock();
        }
    }

}

测试结果:

 

Redisson实现分布式锁第三种实现方式(SpringBoot)

 

pom.xm 引入如下依赖

        
        
            org.redisson
            redisson-spring-boot-starter
            3.12.0
        

 

application.yml 配置 

spring:
  redis:
    host: 192.168.0.218
    port: 6379
    password: 123456

启动类:(需要将导入配置文件注释掉)

@SpringBootApplication
//@ImportResource("classpath*:redisson.xml")
public class RedissonLockApplication {

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

}

 

controller实现

package com.hy.redissonlock.controller;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class RedissonLockController {
    @Resource
    private RedissonClient redisson;

    @RequestMapping("redissonLock")
    public String redissonLock() {
        RLock rLock = redisson.getLock("order");
        log.info("我进入了方法!!");
        try {
            rLock.lock(30, TimeUnit.SECONDS);
            log.info("我获得了锁!!!");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("我释放了锁!!");
            rLock.unlock();
        }
        log.info("方法执行完成!!");
        return "方法执行完成!!";
    }
}

测试

启动项目

基于Redis 的setnx 实现分布式锁 和 Redisson实现分布式锁的三种方式以及实现原理_第4张图片

 

后台日志

基于Redis 的setnx 实现分布式锁 和 Redisson实现分布式锁的三种方式以及实现原理_第5张图片

 

 

zookeeper实现分布式锁的原理及基于zookeeper的 curator 客户端实现分布式锁

你可能感兴趣的:(分布式锁,redisson,redis,java,分布式)