The use of redisson

Time:2021-6-11

The use of redisson

brief introduction

In current projects, there are often concurrency problems. One way to solve the concurrency problem is to use distributed locks. In previous projects, the setnx feature of redis was often used to implement distributed lock, but it may cause deadlock. So, redisson can be used to implement distributed lock of redis. The scenario I use here is SMS captcha service. Only one thread can send SMS to the same mobile phone number at the same time.

Native usage

When we don’t use redisson, we usually use redis to implement distributed lock.

@Component
public class RedissonLockImpl implements RedissonLock {

    private static ThreadLocal<String>  threadLocal = new ThreadLocal<>();
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public boolean tryLock(String key, long timeout, TimeUnit timeUnit) {

        if (threadLocal.get() == null){
            String lockName = "redis:test:name";
            threadLocal.set(lockName);
            return redisTemplate.opsForValue().setIfAbsent(key, lockName, timeout, timeUnit);
        }else if (threadLocal.get().equals(redisTemplate.opsForValue().get(key))){
            return true;
        }
        return false;
    }
}

Then use this in your code:

@RestController
public class RedissonController {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonLock redissonLock;

    @RequestMapping("/submitOrder")
    public String submitOrder(){

        String key = "test";
        //It may exist that the current thread has not finished within the time you set
        boolean lock = redissonLock.tryLock(key, 4, TimeUnit.SECONDS);
        if (!lock){
            return "error";
        }

        try {

            //Specific business logic
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock>0){
                //Order
                stock = stock - 1;
                redisTemplate.opsForValue().set("stock", stock+"");
                return "success";

            } else {
                System. Out. Println ("out of stock");
                return "false";
            }
        } finally {
            //The lock can be released here
        }

    }
}

This may result in that the thread has not finished executing after the lock time you set, but the lock has been released, which will lead to the disorder of the sequence of obtaining and releasing the lock, and cause business problems.

Original improvement

For this kind of thread timeout problem, you can give the thread a new guard thread, and the guard thread will refresh every 10 seconds to complete the life renewal. When the main thread finishes running, the guardian thread will die by itself, so we don’t need to operate( But the general thread is a thread pool, the thread will not die, the guardian thread will not automatically die, so there is a risk).

The use of redisson

Of course, you can manually implement a thread to maintain the life extension time, but it will be very troublesome to implement. So we recommend using reisson directly.Redisson itself has achieved the renewal here.

The specific use of redisson

RLOCK and others in redisson have been renewed to ensure the end of our business execution.

Introduce dependency

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

Write configuration class

@Configuration
public class RedissonConfig {


    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.password}")
    private String password;

    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        //Add master-slave configuration
//        config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
        return Redisson.create(config);
    }
}

Use in code

So we can inject it automaticallyRedissonClientUse it. But generally, we can wrap a layer of service on it to wrap our general exceptions and other scenarios.

/**
 *Redisson operation class
 *@ author Zhu Youde
 */
@Service("redissonService")
public class RedissonService {

    @Autowired
    private RedissonClient redissonClient;

    public void getRedissonClient() throws IOException {
        Config config = redissonClient.getConfig();
        System.out.println(config.toJSON().toString());
    }

    /**`
     *Get string object
     *
     * @param objectName
     * @return
     */
    public <T> RBucket<T> getRBucket(String objectName) {
        RBucket<T> bucket = redissonClient.getBucket(objectName);
        return bucket;
    }

    /**
     *Get map object
     *
     * @param objectName
     * @return
     */
    public <K, V> RMap<K, V> getRMap(String objectName) {
        RMap<K, V> map = redissonClient.getMap(objectName);
        return map;
    }

    /**
     *Get ordered collection
     *
     * @param objectName
     * @return
     */
    public <V> RSortedSet<V> getRSortedSet(String objectName) {
        RSortedSet<V> sortedSet = redissonClient.getSortedSet(objectName);
        return sortedSet;
    }

    /**
     *Get collection
     *
     * @param objectName
     * @return
     */
    public <V> RSet<V> getRSet(String objectName) {
        RSet<V> rSet = redissonClient.getSet(objectName);
        return rSet;
    }

    /**
     *Get list
     *
     * @param objectName
     * @return
     */
    public <V> RList<V> getRList(String objectName) {
        RList<V> rList = redissonClient.getList(objectName);
        return rList;
    }

    /**
     *Get queue
     *
     * @param objectName
     * @return
     */
    public <V> RQueue<V> getRQueue(String objectName) {
        RQueue<V> rQueue = redissonClient.getQueue(objectName);
        return rQueue;
    }

    /**
     *Get double ended queue
     *
     * @param objectName
     * @return
     */
    public <V> RDeque<V> getRDeque(String objectName) {
        RDeque<V> rDeque = redissonClient.getDeque(objectName);
        return rDeque;
    }


    /**
     *Get lock
     * @param objectName
     * @return
     */
    public RLock getRLock(String objectName) {
        RLock rLock = redissonClient.getLock(objectName);
        return rLock;
    }

    public Boolean tryLock(String key, long leaseTime, TimeUnit unit) {
        RLock rLock = redissonClient.getLock(key);
        boolean tryLock = false;
        try {
            tryLock = rLock.tryLock(0, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
        return tryLock;
    }

    public Boolean verifyTryLock(RLock rLock, long leaseTime, TimeUnit unit) {
        boolean tryLock = false;
        try {
            tryLock = rLock.tryLock(0, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
        return tryLock;
    }

    /**
     *Get read lock
     *
     * @param objectName
     * @return
     */
    public RReadWriteLock getRWLock(String objectName) {
        RReadWriteLock rwlock = redissonClient.getReadWriteLock(objectName);
        return rwlock;
    }

    /**
     *Get the number of atoms
     *
     * @param objectName
     * @return
     */
    public RAtomicLong getRAtomicLong(String objectName) {
        RAtomicLong rAtomicLong = redissonClient.getAtomicLong(objectName);
        return rAtomicLong;
    }

    /**
     *Get count lock
     *
     * @param objectName
     * @return
     */
    public RCountDownLatch getRCountDownLatch(String objectName) {
        RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch(objectName);
        return rCountDownLatch;
    }

Use of specific business scenarios

@Service
public class MyService {

    @Autowired
    private RedissonService redissonService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public String sendCodeByTemplate(final String phone){
       
        //Expiration time
        String ttlKey = "redis:ttl:phone:"+phone;
        String key = "redis:phone:"+phone;
        //When sending a message to this mobile phone number, the sending mobile phone number will be locked
        if (!redissonService.tryLock(key, 10, TimeUnit.SECONDS)){
            //Get expiration time
            Long ttl = redisTemplate.getExpire(ttlKey);
            //Send verification code frequently
            Return "send frequently";
        }
        
        //Send verification code
        sendCode(phone);
        return "true";
    }
    
    private boolean sendCode(String phone){
        //Write specific sending logic
        return true;
    }
}

<img style=”zoom:150%;” />

reference resources

reference resources

Recommended Today

In the simplest way, learn about react’s redux

This paper starts with the create react app project and grows from the most basic code to an actual project.Note: This article does not have most theories, only code, which will be gradually extended. Redux components createStore.js reducer.js action.js 1. Let’s talk about the first one, createstore.js Look at the code first import { createStore […]