一个简单的(基于redisson的)分布式同步工具类封装

Posted justry_deng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个简单的(基于redisson的)分布式同步工具类封装相关的知识,希望对你有一定的参考价值。

一个简单的(基于redisson的)分布式同步工具类封装

背景说明

有些分布式同步逻辑不需要作用于整个方法,只需要作用于指定的业务逻辑代码块即可,类似于synchronized代码块。于是有了下面这个简单的封装类。

准备工作

提示:此同步工具类中的redis分布式锁直接采用redisson实现。

第一步:引入redisson依赖

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

第二步:配置RedissonClient

提示:这里的配置以单体redis为例,更多配置详见redisson官网

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RedissonConfig
 *
 * @author JustryDeng
 * @since 2022/3/25 10:33
 */
@Slf4j
@Configuration
public class RedissonConfig 
    
    @Value("$spring.redis.host")
    private String host;
    
    @Value("$spring.redis.port")
    private String port;
    
    @Bean
    public RedissonClient redissonClient() 
        Config config = new Config();
        String address = host + ":" + port;
        log.info("redis address -> ", address);
        config.useSingleServer()
                .setAddress("redis://" + address);
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    

工具类

工具类接口

import lombok.Getter;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * redis锁支持
 *
 * @author JustryDeng
 * @since 2022/4/19 9:36
 */
public interface RedisLockSupport 
    
    /**
     * 执行同步逻辑
     * <br />
     * 此逻辑,应由redis lock保证全局同步
     *
     * @param function
     *            业务逻辑块
     * @param param
     *            参数
     *
     * @throws NotAcquiredRedisLockException 获取redis锁时抛出失败
     * @return 逻辑执行结果
     */
    <P, R> R exec(Function<P, R> function, P param) throws NotAcquiredRedisLockException;
    
    /**
     * 执行同步逻辑
     * <br />
     * 此逻辑,应由redis lock保证全局同步
     *
     * @param function
     *            业务逻辑块
     * @return 执行结果
     *
     * @throws NotAcquiredRedisLockException 获取redis锁时抛出失败
     */
    <R> R exec(NoArgFunction<R> function) throws NotAcquiredRedisLockException;
    
    /**
     * 执行同步逻辑
     * <br />
     * 此逻辑,应由redis lock保证全局同步
     *
     * @param consumer
     *            业务逻辑块
     * @param param
     *            参数
     *
     * @throws NotAcquiredRedisLockException 获取redis锁时抛出失败
     */
    <P> void voidExec(Consumer<P> consumer, P param) throws NotAcquiredRedisLockException;
    
    /**
     * 执行同步逻辑
     * <br />
     * 此逻辑,应由redis lock保证全局同步
     *
     * @param consumer
     *            业务逻辑块
     * @throws NotAcquiredRedisLockException 获取redis锁时抛出失败
     */
    void voidExec(NoArgConsumer consumer) throws NotAcquiredRedisLockException;
    
    /**
     * 获取redis lock失败
     *
     * @author JustryDeng
     * @since 2022/4/19 10:44
     */
    @Getter
    class NotAcquiredRedisLockException extends RuntimeException
        
        /** 锁 key */
        private final String lockKey;
        
        /** 等待获取锁的最大时长 */
        private final long waitTime;
        
        public NotAcquiredRedisLockException(String lockKey, long waitTime) 
            super(String.format("lockKey=%s, waitTime=%d", lockKey, waitTime));
            this.lockKey = lockKey;
            this.waitTime = waitTime;
        
    
    

工具类接口的默认实现

import lombok.Getter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * redis分布式锁默认实现
 * <p>
 *     使用示例见@link com.ideaaedi.heywebuy.srv.RedisLockSupportTest
 * </p>
 *
 * @author JustryDeng
 * @since 2022/4/19 10:08
 */
@Getter
public class DefaultRedisLockSupport implements RedisLockSupport 
    
    /** 默认的redisson客户端 */
    private static volatile RedissonClient defaultRedissonClient;
    
    /** redisson客户端(优先级高于defaultRedissonClient,当redissonClient不为null时,使用redissonClient) */
    protected RedissonClient redissonClient;
    
    /** 锁 key */
    protected final String lockKey;
    
    /** 等待获取锁的最大时长 */
    protected long waitTime = 1L;
    
    /** 释放锁的最大时长 */
    protected long leaseTime = 3L;
    
    /** WaitTime和LeaseTime的时间单位 */
    protected TimeUnit unit = TimeUnit.SECONDS;
    
    public DefaultRedisLockSupport(String lockKey) 
        this.lockKey = lockKey;
    
    
    public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey) 
        this.redissonClient = redissonClient;
        this.lockKey = lockKey;
    
    
    public DefaultRedisLockSupport(String lockKey, long waitTime, long leaseTime) 
        this.lockKey = lockKey;
        this.waitTime = waitTime;
        this.leaseTime = leaseTime;
    
    
    public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey, long waitTime, long leaseTime) 
        this.redissonClient = redissonClient;
        this.lockKey = lockKey;
        this.waitTime = waitTime;
        this.leaseTime = leaseTime;
    
    
    public DefaultRedisLockSupport(String lockKey, long waitTime, long leaseTime, TimeUnit unit) 
        this.lockKey = lockKey;
        this.waitTime = waitTime;
        this.leaseTime = leaseTime;
        this.unit = unit;
    
    
    public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey, long waitTime, long leaseTime, TimeUnit unit) 
        this.redissonClient = redissonClient;
        this.lockKey = lockKey;
        this.waitTime = waitTime;
        this.leaseTime = leaseTime;
        this.unit = unit;
    
    
    @Override
    public <P, R> R exec(Function<P, R> function, P param) throws NotAcquiredRedisLockException 
        RedissonClient client = redissonClient();
        RLock lock = client.getLock(lockKey);
        boolean obtainLock = false;
        try 
            obtainLock = lock.tryLock(waitTime, leaseTime, unit);
         catch (InterruptedException e) 
            // ignore
        
        if (obtainLock) 
            try 
                return function.apply(param);
             finally 
                if (lock.isHeldByCurrentThread()) 
                    lock.unlock();
                
            
        
        throw new NotAcquiredRedisLockException(lockKey, waitTime);
    
    
    @Override
    public <R> R exec(NoArgFunction<R> function) throws NotAcquiredRedisLockException 
        RedissonClient client = redissonClient();
        RLock lock = client.getLock(lockKey);
        boolean obtainLock = false;
        try 
            obtainLock = lock.tryLock(waitTime, leaseTime, unit);
         catch (InterruptedException e) 
            // ignore
        
        if (obtainLock) 
            try 
                return function.apply();
             finally 
                if (lock.isHeldByCurrentThread()) 
                    lock.unlock();
                
            
        
        throw new NotAcquiredRedisLockException(lockKey, waitTime);
    
    
    @Override
    public <P> void voidExec(Consumer<P> consumer, P param) throws NotAcquiredRedisLockException 
        RedissonClient client = redissonClient();
        RLock lock = client.getLock(lockKey);
        boolean obtainLock = false;
        try 
            obtainLock = lock.tryLock(waitTime, leaseTime, unit);
         catch (InterruptedException e) 
            // ignore
        
        if (obtainLock) 
            try 
                consumer.accept(param);
                return;
             finally 
                if (lock.isHeldByCurrentThread()) 
                    lock.unlock();
                
            
        
        throw new NotAcquiredRedisLockException(lockKey, waitTime);
    
    
    @Override
    public void voidExec(NoArgConsumer consumer) throws NotAcquiredRedisLockException 
        RedissonClient client = redissonClient();
        RLock lock = client.getLock(lockKey);
        boolean obtainLock = false;
        try 
            obtainLock = lock.tryLock(waitTime, leaseTime, unit);
         catch (InterruptedException e) 
            // ignore
        
        if (obtainLock) 
            try 
                consumer.accept();
                return;
             finally 
                if (lock.isHeldByCurrentThread()) 
                    lock.unlock();
                
            
        
        throw new NotAcquiredRedisLockException(lockKey, waitTime);
    
    
    /**
     * 获取RedissonClient实例
     *
     * @return  RedissonClient实例
     */
    protected RedissonClient redissonClient() 
        if (this.redissonClient != null) 
            return this.redissonClient;
        
        if (DefaultRedisLockSupport.defaultRedissonClient != null) 
            return DefaultRedisLockSupport.defaultRedissonClient;
        
        throw new IllegalStateException("There is not redissonClient available.");
    
    
    /**
     * 初始化默认的Redisson客户端
     *
     * @param redissonClient
     *            Redisson客户端实例
     */
    public static void initDefaultRedissonClient(RedissonClient redissonClient) 
        if (DefaultRedisLockSupport.defaultRedissonClient != null && !DefaultRedisLockSupport.defaultRedissonClient.equals(redissonClient)) 
            throw new IllegalStateException("defaultRedissonClient already been initialized.");
        
        synchronized (DefaultRedisLockSupport.class) 
            if (DefaultRedisLockSupport.defaultRedissonClient != null) 
                if (DefaultRedisLockSupport.defaultRedissonClient.equals(redissonClient)) 
                    return;
                
                throw new IllegalStateException("defaultRedissonClient already been initialized.");
            
            DefaultRedisLockSupport.defaultRedissonClient = redissonClient;
        
    

工具类接口涉及到的两个其它接口

  • NoArgConsumer

    /**
     * 无参 Consumer
     *
     * @author JustryDeng
     * @since 2022/4/19 11:17
     */
    @FunctionalInterface
    public interface NoArgConsumer 
        
        /**
         * 执行逻辑
         */
        void accept();
    
    
  • NoArgFunction

    /**
     * 无参 Function
     *
     * @author JustryDeng
     * @since 2022/4/19 11:17
     */
    @FunctionalInterface
    public interface NoArgFunction<R> 
        
        /**
         * 执行逻辑
         *
         * @return 执行结果
         */
        R apply();
    
    

使用示例

import com.ideaaedi.heywebuy.srv.config.redisson.DefaultRedisLockSupport;
import com.ideaaedi.heywebuy.srv.config.redisson.NoArgConsumer;
import com.ideaaedi.heywebuy.srv.config.redisson.NoArgFunction;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.redisson.api.RedissonClient;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

@Slf4j
@SpringBootTest
public class RedisLockSupportTest 
    
    @Resource
    private RedissonClient redissonClient;
    
    
    @BeforeEach
    void initMethod() 
        // 在项目启动时注入
        DefaultRedisLockSupport.initDefaultRedissonClient(redissonClient);
    
    
    @Test
    void test() 
        // 无参数 无返回值 NoArgConsumer
        new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).voidExec(new NoArgConsumer() 
            @Override
            public void accept() 
                // 我是业务逻辑
                System.out.println("[无参数 无返回值 NoArgConsumer]:\\t\\t\\t\\t\\t" + 111111);
            
        );
        
        // 有参数 无返回值 Consumer
        new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).voidExec(new Consumer<Integer>() 
            @Override
            public void accept(Integer s) 
                // 我是业务逻辑
                System.out.println("[有参数 无返回值 Consumer]:\\t\\t\\t\\t\\t" + s);
            
        , 2222);
    
    
        // 无参数 有返回值 NoArgFunction
        Map<String, Object> result = new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120)
                .exec(new NoArgFunction<Map<String, Object>>() 
                    @Override
                    public Map<以上是关于一个简单的(基于redisson的)分布式同步工具类封装的主要内容,如果未能解决你的问题,请参考以下文章

redisson 应用(一)

Redisson入门教程

Redisson教程

redis分布式锁实现---基于redisson封装自己的分布式锁

SpringBoot集成redisson操作redis

高并发你知道吗?大家都在使用Redisson实现分布式锁了!!