redis实现分布式锁-redisson

1、引入包:

2、redis在yml中的配置:

因为本地环境和测试环境的redis部署方式不同,本地为单节点,测试为集群部署方式,所以application-dev.yml和application-test.yml中的redis配置不同。

dev.yml中的配置:单节点的配置方式

test.yml中的配置:集群的配置方式

3、配置RedissonConfig:这里根据不同的环境读取不同的redis配置,并创建RedissonClient 。

import lombok.extern.slf4j.Slf4j;

import org.redisson.Redisson;

import org.redisson.api.RedissonClient;

import org.redisson.config.ClusterServersConfig;

import org.redisson.config.Config;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.io.DefaultResourceLoader;

import org.springframework.core.io.Resource;

import org.yaml.snakeyaml.Yaml;

import java.io.IOException;

import java.io.InputStream;

import java.util.Map;

/**

* redisson配置

*/

@Configuration

@Slf4j

public class RedissonConfig {

    @Value("${env}")

    private String env;

    @Bean

    public RedissonClient redissonClient() {

        Config config = new Config();

        String redisNodes = this.getRedisNodes();

        if (env.equals("dev")) {

            config.useSingleServer().setAddress("redis://127.0.0.1:6379");

        } else if (env.equals("test")) {

            String[] redisNodeList = redisNodes.replace("[","").replace("]","").split(",");

            // 指定使用集群部署方式

            ClusterServersConfig clusterServersConfig = config.useClusterServers()

                    // 集群状态扫描间隔时间,单位是毫秒

                    .setScanInterval(2000);

            // 添加节点

            for (String node : redisNodeList) {

                clusterServersConfig.addNodeAddress("redis://"+node.trim());

            }

        } else if (env.equals("prod")){

            String[] redisNodeList = redisNodes.replace("[","").replace("]","").split(",");

            ClusterServersConfig clusterServersConfig = config.useClusterServers().setScanInterval(2000);

            for (String node : redisNodeList) {

                clusterServersConfig.addNodeAddress("redis://"+node.trim());

            }

        }

        RedissonClient client = Redisson.create(config);

        return client;

    }

    // 读取application-test.yml文件中的redis集群节点

    private String getRedisNodes() {

        Map map = null;

        Yaml yaml = new Yaml();

        //文件路径是相对类目录(src/main/java)的相对路径

        Resource resource = new DefaultResourceLoader().getResource("classpath:application-test.yml");

        try {

            InputStream inputStream = resource.getInputStream();

            map = (Map) yaml.load(inputStream);

            // map: {spring={redis={timeout=30000, password=null, cluster={nodes=[127.0.0.1:6001, 127.0.0.1:6002, 127.0.0.1:6003, 127.0.0.1:7001, 127.0.0.1:7002, 127.0.0.1:7003], max-redirects=3}, database=0, lettuce={pool={max-active=1000, max-idle=10, max-wait=-1, min-idle=5}}}}, env=test}

            String nodes = ((Map)((Map)((Map) map.get("spring")).get("redis")).get("cluster")).get("nodes").toString();

            System.out.println(nodes);

            return nodes;

        } catch (IOException e) {

            e.printStackTrace();

        }

        return null;

    }

}

4、使用RedissonClient :

@Autowired

private RedissonClient redissonClient;

5、RedissonClient的使用:

建议业务逻辑处理部分的处理时间不宜过长。 默认使用的是非公平锁

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.redisson.api.RLock;

import org.redisson.api.RedissonClient;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**

* @ClassName: RedissonLockService

* @Description: 测试redisson分布式锁

*/

@Service

@Slf4j

public class RedissonLockService {

    @Autowired

    private RedissonClient redissonClient;

    @Autowired

    private RedisTemplate redisTemplate;

    /**

    * 取出数据,逻辑处理后保存

    */

    @Transactional

    public Integer getAndSave() throws Exception {

        Integer result = null;

        // 得到具体的锁

        RLock lock = redissonClient.getLock("test-redisson-lock");

        // 尝试加锁,最多等待15秒,上锁以后10秒自动解锁

        Boolean lockRes = lock.tryLock(15, 10, TimeUnit.SECONDS);

        // 如果拿到了锁

        if (lockRes) {

            // 业务逻辑处理

            try {

                String value = redisTemplate.opsForValue().get("redissionValue");

                if (StringUtils.isNotEmpty(value)) {

                    int addValue = Integer.valueOf(value) + 1;

                    result = addValue;

                    redisTemplate.opsForValue().set("redissionValue", String.valueOf(addValue));

                } else {

                    result = 1;

                    redisTemplate.opsForValue().set("redissionValue", "1");

                }

            } catch (Exception e) {

                throw new Exception(e);

            } finally {

                // 释放锁

                lock.unlock();

            }

        }

        return result;

    }

}

6、测试方法:使用线程池,开启多个线程,对redis中的值进行100次的+1操作。

import com.loong.redis.service.RedissonLockService;

import lombok.extern.slf4j.Slf4j;

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;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

/**

* @ClassName: TestRedissonController

* @Description: 测试Redisson

* @author: sunzf

* @date: 2021/11/23

*/

@Slf4j

@RestController

@RequestMapping("/redisson")

public class TestRedissonController {

    @Autowired

    private RedissonLockService redissonLockService;

    /**

    * 开启多个线程,测试redisson分布式锁

    */

    @GetMapping("/test/lock")

    public void testLock() {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        for (int i = 1; i <= 100; i++) {

            Future future = executor.submit(new Callable() {

                @Override

                public Integer call() throws Exception {

                    Integer result = 0;

                    try {

                        result = redissonLockService.getAndSave();

                        long currentThreadId = Thread.currentThread().getId();

                        System.out.println("当前线程ID: " + currentThreadId + ", 结果: " + result);

                    } catch (Exception e) {

                        log.error("测试异常, e: {}", e.getMessage());

                    }

                    return result;

                }

            });

        }

        executor.shutdown();

    }

}

7、启动服务,请求这个测试接口,测试结果:

从测试结果可以看出,多个线程并发执行,最终数据的结果是正确的,从而实现了Redisson的基本使用。

当前线程ID: 94, 结果: 1

当前线程ID: 94, 结果: 2

当前线程ID: 95, 结果: 3

当前线程ID: 94, 结果: 4

当前线程ID: 95, 结果: 5

当前线程ID: 95, 结果: 6

当前线程ID: 95, 结果: 7

当前线程ID: 95, 结果: 8

当前线程ID: 95, 结果: 9

当前线程ID: 95, 结果: 10

当前线程ID: 95, 结果: 11

当前线程ID: 95, 结果: 12

当前线程ID: 95, 结果: 13

当前线程ID: 95, 结果: 14

当前线程ID: 95, 结果: 15

当前线程ID: 95, 结果: 16

当前线程ID: 95, 结果: 17

当前线程ID: 95, 结果: 18

当前线程ID: 95, 结果: 19

当前线程ID: 95, 结果: 20

当前线程ID: 95, 结果: 21

当前线程ID: 95, 结果: 22

当前线程ID: 95, 结果: 23

当前线程ID: 95, 结果: 24

当前线程ID: 95, 结果: 25

当前线程ID: 95, 结果: 26

当前线程ID: 95, 结果: 27

当前线程ID: 95, 结果: 28

当前线程ID: 95, 结果: 29

当前线程ID: 95, 结果: 30

当前线程ID: 95, 结果: 31

当前线程ID: 95, 结果: 32

当前线程ID: 95, 结果: 33

当前线程ID: 95, 结果: 34

当前线程ID: 95, 结果: 35

当前线程ID: 95, 结果: 36

当前线程ID: 95, 结果: 37

当前线程ID: 95, 结果: 38

当前线程ID: 95, 结果: 39

当前线程ID: 95, 结果: 40

当前线程ID: 95, 结果: 41

当前线程ID: 98, 结果: 42

当前线程ID: 98, 结果: 43

当前线程ID: 98, 结果: 44

当前线程ID: 98, 结果: 45

当前线程ID: 98, 结果: 46

当前线程ID: 98, 结果: 47

当前线程ID: 98, 结果: 48

当前线程ID: 98, 结果: 49

当前线程ID: 98, 结果: 50

当前线程ID: 98, 结果: 51

当前线程ID: 98, 结果: 52

当前线程ID: 98, 结果: 53

当前线程ID: 98, 结果: 54

当前线程ID: 98, 结果: 55

当前线程ID: 98, 结果: 56

当前线程ID: 98, 结果: 57

当前线程ID: 98, 结果: 58

当前线程ID: 98, 结果: 59

当前线程ID: 98, 结果: 60

当前线程ID: 98, 结果: 61

当前线程ID: 98, 结果: 62

当前线程ID: 98, 结果: 63

当前线程ID: 98, 结果: 64

当前线程ID: 98, 结果: 65

当前线程ID: 98, 结果: 66

当前线程ID: 98, 结果: 67

当前线程ID: 98, 结果: 68

当前线程ID: 98, 结果: 69

当前线程ID: 98, 结果: 70

当前线程ID: 97, 结果: 71

当前线程ID: 97, 结果: 72

当前线程ID: 94, 结果: 73

当前线程ID: 94, 结果: 74

当前线程ID: 94, 结果: 75

当前线程ID: 94, 结果: 76

当前线程ID: 94, 结果: 77

当前线程ID: 94, 结果: 78

当前线程ID: 94, 结果: 79

当前线程ID: 94, 结果: 80

当前线程ID: 94, 结果: 81

当前线程ID: 94, 结果: 82

当前线程ID: 94, 结果: 83

当前线程ID: 94, 结果: 84

当前线程ID: 94, 结果: 85

当前线程ID: 94, 结果: 86

当前线程ID: 94, 结果: 87

当前线程ID: 94, 结果: 88

当前线程ID: 94, 结果: 89

当前线程ID: 94, 结果: 90

当前线程ID: 94, 结果: 91

当前线程ID: 94, 结果: 92

当前线程ID: 94, 结果: 93

当前线程ID: 94, 结果: 94

当前线程ID: 94, 结果: 95

当前线程ID: 94, 结果: 96

当前线程ID: 97, 结果: 97

当前线程ID: 95, 结果: 98

当前线程ID: 98, 结果: 99

当前线程ID: 96, 结果: 100

redis中的结果:


其他要点:

1、大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

修改lockWatchdogTimeout :

Config config = new Config();

config.setLockWatchdogTimeout(50000L);

注意:看门狗可能会影响性能。

2、Redisson非公平锁的第二种用法:lock.lock()

/**

* 锁的第二种用法:

* 取出数据,逻辑处理后保存

*/

@Transactional

public Integer getAndSave2() throws Exception {

    Integer result = null;

    // 得到具体的锁

    RLock lock = redissonClient.getLock("test-redisson-lock-2");

    // 业务逻辑处理

    try {

        // 加锁以后10秒钟自动解锁

        // 无需调用unlock方法手动解锁

        lock.lock(10, TimeUnit.SECONDS);

        String value = redisTemplate.opsForValue().get("redissionValue2");

        if (StringUtils.isNotEmpty(value)) {

            int addValue = Integer.valueOf(value) + 1;

            result = addValue;

            redisTemplate.opsForValue().set("redissionValue2", String.valueOf(addValue));

        } else {

            result = 1;

            redisTemplate.opsForValue().set("redissionValue2", "1");

        }

    } catch (Exception e) {

        throw new Exception(e);

    } finally {

        // 释放锁

        lock.unlock();

    }

    return result;

}



参考:

https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers

https://github.com/redisson/redisson/wiki/8.-分布式锁和同步器

https://www.cnblogs.com/qdhxhz/p/11046905.html

https://www.cnblogs.com/cjsblog/p/11273205.html

你可能感兴趣的:(redis实现分布式锁-redisson)