springboot集成redisson实现分布式锁

d_hero
发布于 2023-6-29 13:58
浏览
0收藏

springboot中集成redisson-spring-boot-starter,

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>
    <groupId>com.example</groupId>
    <artifactId>boot-redisson</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>boot-redisson</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.boot.redisson.BootRedissonApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

单机配置,和集权配置

单机:

application.yml

spring:
  redis:
    port: 6379
    host: 127.0.0.1
    password: "123456"
    lettuce:
      pool:
        # 连接池中的最大空闲连接 默认8
        max-idle: 8
        # 连接池中的最小空闲连接 默认0
        min-idle: 0
        # 连接池最大连接数 默认8 ,负数表示没有限制
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
        max-wait: -1

集群配置:application.yml

spring:
  redis:
    password: '123456'  #Redis服务器的登录密码
    cluster:
      enable: true
      max-redirects: 5
      nodes:
        - 10.100.232.181:6379
        - 10.100.232.181:6380
        - 10.100.232.182:6379
        - 10.100.232.182:6380
        - 10.100.232.183:6379
        - 10.100.232.183:6380

和普通集成redis的配置一样。以前集成redis的,不需要修改

分布式锁接口定义

package com.boot.redisson.lock;

import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

/**
 * 描述: 锁抽象接口 <br>
 * 时间: 2021-06-22 9:29  <br>
 * 作者:IT学习道场
 */
public interface IRedissonLocker {
    /**
     * 加锁
     * @param lockKey
     * @return
     */
    RLock lock(String lockKey);

    /**
     * 加锁,并且设置过期时间
     * @param lockKey 锁 key
     * @param timeout 过期时间 单位默认 = 秒
     * @return
     */
    RLock lock(String lockKey, int timeout);

    /**
     * 加锁
     * @param lockKey   锁 key
     * @param unit      过期时间
     * @param timeout   时间单位
     * @return
     */
    RLock lock(String lockKey, TimeUnit unit, int timeout);

    /**
     * 常识加锁
     * @param lockKey       锁 key
     * @param unit          时间单位
     * @param waitTime      最长等待取锁时间,如果再这个时间内取到锁将返回true,如果超过这个时间还没取到锁将返回false
     * @param leaseTime     取到锁之后锁过期时间,当超过这个时间还没执行完业务锁将被释放。
     * @return
     */
    boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);


    /**
     * 释放锁
     * @param lockKey
     */
    void unlock(String lockKey);

    /**
     * 释放锁
     * @param lock
     */
    void unlock(RLock lock);

    /**
     * 强制释放锁
     * @param lockKey
     */
    void forceUnlock(String lockKey);
    /**
     * 强制释放锁
     * @param lock
     */
    void forceUnlock(RLock lock);


    /**
     * 公平加锁
     * @param lockKey
     * @return
     */
    RLock fairLock(String lockKey);

    /**
     * 公平加锁,并且设置过期时间
     * @param lockKey 锁 key
     * @param timeout 过期时间 单位默认 = 秒
     * @return
     */
    RLock fairLock (String lockKey, int timeout);

    /**
     * 公平加锁
     * @param lockKey   锁 key
     * @param unit      过期时间
     * @param timeout   时间单位
     * @return
     */
    RLock fairLock (String lockKey, TimeUnit unit, int timeout);


    /**
     * 红锁加锁
     * @param lockKey
     * @return
     */
    RLock redLock(String lockKey);

    /**
     * 红锁加锁,并且设置过期时间
     * @param lockKey 锁 key
     * @param timeout 过期时间 单位默认 = 秒
     * @return
     */
    RLock redLock (String lockKey, int timeout);

    /**
     * 红锁加锁
     * @param lockKey   锁 key
     * @param unit      过期时间
     * @param timeout   时间单位
     * @return
     */
    RLock redLock (String lockKey, TimeUnit unit, int timeout);

}

redisson的锁实现

package com.boot.redisson.lock;

import org.redisson.RedissonRedLock;
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;

/**
 * 描述: Redisson分布式锁实现 <br>
 * 时间: 2021-06-22 9:30  <br>
 * 作者:IT学习道场
 */
@Component
public class RedissonLocker implements IRedissonLocker {
    @Autowired
    private RedissonClient redissonClient;

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

    @Override
    public RLock lock(String lockKey, int leaseTime) {
        RLock lock= redissonClient.getLock(lockKey);
        lock.lock(leaseTime, 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;
    }

    @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) {
            return false;
        }
    }

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

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

    }

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

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

    @Override
    public RLock fairLock(String lockKey) {
        RLock fairLock = redissonClient.getFairLock(lockKey);
        fairLock.lock();
        return fairLock;
    }

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

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

    @Override
    public RLock redLock(String lockKey) {
        RLock lock= redissonClient.getLock(lockKey);
        RedissonRedLock redLock = new RedissonRedLock(lock);
        redLock.lock();
        return redLock;
    }

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

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


}

使用实例:

package com.boot.redisson.controller;

import com.boot.redisson.lock.RedissonLocker;
import org.redisson.api.RLock;
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.TimeUnit;

/**
 * 描述: RedissonController <br>
 * 时间: 2021-06-22 8:43  <br>
 * 作者:IT学习道场
 */
@RestController
@RequestMapping("/redisson")
public class RedissonController {

    @Autowired
    private RedissonLocker locker;

    @GetMapping("/lock")
    private void lock(){
        RLock lock = locker.lock("lock");
        try {
            if( lock.isLocked() ) {
                System.out.println("拿到锁了开始执行业务");
                Thread.sleep(30000);
            }

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    @GetMapping("/tryLock")
    private void tryLock(){
        boolean locked = locker.tryLock("tryLock", TimeUnit.SECONDS, 20, 10);
        try {
            if( locked ) {
                System.out.println("拿到锁了开始执行业务");
                Thread.sleep(30000);
            }

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            locker.unlock("tryLock");
        }
    }
}

这里另外赠送 RedissonClient和 RedisTemplate同步序列化,都采用jackson,实现RedissonClient和 RedisTemplate相互存取数据

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 描述: RedisTemplateConfig <br>
 * 时间: 2021-06-22 9:16  <br>
 * 作者:IT学习道场
 */
@Configuration
public class RedisTemplateConfig {

    @Autowired(required = false)
    private List<RedissonAutoConfigurationCustomizer> redissonAutoConfigurationCustomizers;

    @Autowired
    private RedissonProperties redissonProperties;

    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private ApplicationContext ctx;


    /**
     * redisTemplate 相关配置
     *
     * @param redissonConnectionFactory redissonConnectionFactory
     * @return org.springframework.data.redis.core.RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedissonConnectionFactory redissonConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redissonConnectionFactory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJackson2();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        return redisTemplate;
    }



    //init jackson2
    private Jackson2JsonRedisSerializer<Object> initJackson2() {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        return jackson2JsonRedisSerializer;
    }




    @Bean
    @ConditionalOnMissingBean(RedisConnectionFactory.class)
    public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
        return new RedissonConnectionFactory(redisson);
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(RedissonClient.class)
    public RedissonClient redisson() throws IOException {
        Config config = null;
        Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
        Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
        Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, redisProperties);
        int timeout;
        if(null == timeoutValue){
            timeout = 10000;
        }else if (!(timeoutValue instanceof Integer)) {
            Method millisMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis");
            timeout = ((Long) ReflectionUtils.invokeMethod(millisMethod, timeoutValue)).intValue();
        } else {
            timeout = (Integer)timeoutValue;
        }

        if (redissonProperties.getConfig() != null) {
            try {
                InputStream is = getConfigStream();
                config = Config.fromJSON(is);
            } catch (IOException e) {
                // trying next format
                try {
                    InputStream is = getConfigStream();
                    config = Config.fromYAML(is);
                } catch (IOException e1) {
                    throw new IllegalArgumentException("Can't parse config", e1);
                }
            }
        } else if (redisProperties.getSentinel() != null) {
            Method nodesMethod = ReflectionUtils.findMethod(RedisProperties.Sentinel.class, "getNodes");
            Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, redisProperties.getSentinel());

            String[] nodes;
            if (nodesValue instanceof String) {
                nodes = convert(Arrays.asList(((String)nodesValue).split(",")));
            } else {
                nodes = convert((List<String>)nodesValue);
            }

            config = new Config();
            config.useSentinelServers()
                    .setMasterName(redisProperties.getSentinel().getMaster())
                    .addSentinelAddress(nodes)
                    .setDatabase(redisProperties.getDatabase())
                    .setConnectTimeout(timeout)
                    .setPassword(redisProperties.getPassword());
        } else if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) {
            Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties);
            Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
            List<String> nodesObject = (List) ReflectionUtils.invokeMethod(nodesMethod, clusterObject);

            String[] nodes = convert(nodesObject);

            config = new Config();
            config.useClusterServers()
                    .addNodeAddress(nodes)
                    .setConnectTimeout(timeout)
                    .setPassword(redisProperties.getPassword());
        } else {
            config = new Config();
            String prefix = "redis://";
            Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
            if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, redisProperties)) {
                prefix = "rediss://";
            }

            config.useSingleServer()
                    .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
                    .setConnectTimeout(timeout)
                    .setDatabase(redisProperties.getDatabase())
                    .setPassword(redisProperties.getPassword());
        }
        if (redissonAutoConfigurationCustomizers != null) {
            for (RedissonAutoConfigurationCustomizer customizer : redissonAutoConfigurationCustomizers) {
                customizer.customize(config);
            }
        }
        config.setCodec(codec());
        return Redisson.create(config);
    }

    private String[] convert(List<String> nodesObject) {
        List<String> nodes = new ArrayList<String>(nodesObject.size());
        for (String node : nodesObject) {
            if (!node.startsWith("redis://") && !node.startsWith("rediss://")) {
                nodes.add("redis://" + node);
            } else {
                nodes.add(node);
            }
        }
        return nodes.toArray(new String[nodes.size()]);
    }

    private InputStream getConfigStream() throws IOException {
        Resource resource = ctx.getResource(redissonProperties.getConfig());
        InputStream is = resource.getInputStream();
        return is;
    }


    private Codec codec(){
        //使用json序列化方式
        Codec codec = new JsonJacksonCodec();
        return codec;
    }



}

自己去试试吧




文章转载自公众号:IT学习道场

分类
标签
已于2023-6-29 13:58:59修改
收藏
回复
举报
回复
    相关推荐