当前位置:网站首页>A simple (redisson based) distributed synchronization tool class encapsulation

A simple (redisson based) distributed synchronization tool class encapsulation

2022-04-23 19:26:00 justry_ deng

A simple ( be based on redisson Of ) Distributed synchronization tool class encapsulation

The background that

Some distributed synchronization logic does not need to act on the entire method , Just act on the specified business logic code block , Be similar to synchronized Code block . So there is the following simple encapsulation class .

preparation

Tips : In this synchronization tool class redis Distributed lock directly adopts redisson Realization .

First step : introduce redisson rely on

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

The second step : To configure RedissonClient

Tips : The configuration here is based on monomer redis For example , For more details, please refer to redisson Official website .

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;
    }
}

Tool class

Tool class interface

import lombok.Getter;

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

/** * redis Lock support  * * @author JustryDeng * @since 2022/4/19 9:36 */
public interface RedisLockSupport {
    
    
    /** *  Execute synchronization logic  * <br /> *  This logic , Should be redis lock Ensure global synchronization  * * @param function *  Business logic block  * @param param *  Parameters  * * @throws NotAcquiredRedisLockException  obtain redis Failed to throw lock  * @return  Logical execution results  */
    <P, R> R exec(Function<P, R> function, P param) throws NotAcquiredRedisLockException;
    
    /** *  Execute synchronization logic  * <br /> *  This logic , Should be redis lock Ensure global synchronization  * * @param function *  Business logic block  * @return  Execution results  * * @throws NotAcquiredRedisLockException  obtain redis Failed to throw lock  */
    <R> R exec(NoArgFunction<R> function) throws NotAcquiredRedisLockException;
    
    /** *  Execute synchronization logic  * <br /> *  This logic , Should be redis lock Ensure global synchronization  * * @param consumer *  Business logic block  * @param param *  Parameters  * * @throws NotAcquiredRedisLockException  obtain redis Failed to throw lock  */
    <P> void voidExec(Consumer<P> consumer, P param) throws NotAcquiredRedisLockException;
    
    /** *  Execute synchronization logic  * <br /> *  This logic , Should be redis lock Ensure global synchronization  * * @param consumer *  Business logic block  * @throws NotAcquiredRedisLockException  obtain redis Failed to throw lock  */
    void voidExec(NoArgConsumer consumer) throws NotAcquiredRedisLockException;
    
    /** *  obtain redis lock Failure  * * @author JustryDeng * @since 2022/4/19 10:44 */
    @Getter
    class NotAcquiredRedisLockException extends RuntimeException{
    
    
        /**  lock  key */
        private final String lockKey;
    
        /**  The maximum length of time waiting to acquire a lock  */
        private final long waitTime;
    
        /** waitTime Time unit of  */
        private final TimeUnit timeUnit;
    
        public NotAcquiredRedisLockException(String lockKey, long waitTime, TimeUnit timeUnit) {
    
            super(String.format("lockKey=%s, waitTime=%d, timeUnit=%d", lockKey, waitTime, timeUnit));
            this.lockKey = lockKey;
            this.waitTime = waitTime;
            this.timeUnit = timeUnit;
        }
    }
    
}

Default implementation of tool class interface

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 Distributed locks are implemented by default  * <p> *  See... For an example {@link com.ideaaedi.heywebuy.srv.RedisLockSupportTest} * </p> * * @author JustryDeng * @since 2022/4/19 10:08 */
@Getter
public class DefaultRedisLockSupport implements RedisLockSupport {
    
    
    /**  default redisson client  */
    private static volatile RedissonClient defaultRedissonClient;
    
    /** redisson client ( Priority over defaultRedissonClient, When redissonClient Not for null when , Use redissonClient) */
    protected RedissonClient redissonClient;
    
    /**  lock  key */
    protected final String lockKey;
    
    /**  The maximum length of time waiting to acquire a lock  */
    protected long waitTime = 1L;
    
    /**  Maximum time to release the lock  */
    protected long leaseTime = 3L;
    
    /** WaitTime and LeaseTime Time unit of  */
    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, unit);
    }
    
    @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, unit);
    }
    
    @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, unit);
    }
    
    @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, unit);
    }
    
    /** *  obtain RedissonClient example  * * @return RedissonClient example  */
    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.");
    }
    
    /** *  Initialize the default Redisson client  * * @param redissonClient * Redisson Client instance  */
    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;
        }
    }
}

Two other interfaces involved in the tool class interface

  • NoArgConsumer

    /** *  No arguments  Consumer * * @author JustryDeng * @since 2022/4/19 11:17 */
    @FunctionalInterface
    public interface NoArgConsumer {
          
        
        /** *  Perform logical  */
        void accept();
    }
    
  • NoArgFunction

    /** *  No arguments  Function * * @author JustryDeng * @since 2022/4/19 11:17 */
    @FunctionalInterface
    public interface NoArgFunction<R> {
          
        
        /** *  Perform logical  * * @return  Execution results  */
        R apply();
    }
    

Examples of use

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() {
    
        //  Inject... When the project starts 
        DefaultRedisLockSupport.initDefaultRedissonClient(redissonClient);
    }
    
    @Test
    void test() {
    
        //  No parameter   No return value  NoArgConsumer
        new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).voidExec(new NoArgConsumer() {
    
            @Override
            public void accept() {
    
                //  I'm business logic 
                System.out.println("[ No parameter   No return value  NoArgConsumer]:\t\t\t\t\t" + 111111);
            }
        });
        
        //  With parameters   No return value  Consumer
        new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).voidExec(new Consumer<Integer>() {
    
            @Override
            public void accept(Integer s) {
    
                //  I'm business logic 
                System.out.println("[ With parameters   No return value  Consumer]:\t\t\t\t\t" + s);
            }
        }, 2222);
    
    
        //  No parameter   There is a return value  NoArgFunction
        Map<String, Object> result = new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120)
                .exec(new NoArgFunction<Map<String, Object>>() {
    
                    @Override
                    public Map<String, Object> apply() {
    
                        //  I'm business logic 
                        return Collections.singletonMap("k1", "v1");
                    }
                } );
        System.out.println("[ No parameter   There is a return value  Function]:\t\t\t\t\t" + result);
    
        //  With parameters   There is a return value  Function
         result = new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).exec(new Function<String, Map<String, Object>>() {
    
            @Override
            public Map<String, Object> apply(String s) {
    
                //  I'm business logic 
                return Collections.singletonMap("k2", s);
            }
        }, "v2");
        System.out.println("[ With parameters   There is a return value  Function]:\t\t\t\t\t" + result);
    }
}

Related information

版权声明
本文为[justry_ deng]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231923567429.html