Spring boot uses redisson to realize distributed lock of redis (2)

 Continued from the previous article http://liaoke0123.iteye.com/blog/2375469

 

Some friends may ask, what if one acquires a lock and performs a time-consuming task, and the time-consuming task takes longer than the default lock release time of the lock.

 

In fact, redisson has automatically released locks to avoid starvation locks.

 

In terms of timeout, our business can not allow it, so I added a fallback strategy.

 

The same distributed lock callback interface

 

 

package com.example.demo.redis2;

import javax.persistence.Transient;

/**
 * Distributed lock callback interface
 *
 * @author lk
 */
public interface DistributedLockCallback<T> {

    /**
     * The caller must implement the business logic that requires distributed locks in this method
     *
     * @return
     */
    public T process();

    /**
     * Caller business
     * Service downgrade when timeout
     * used with process
     */
    public T fallback();

    /**
     * Get the distributed lock name
     *
     * @return
     */
    public String getLockName();
}

 You can see that I created a new fallback interface

 

 

package com.example.demo.redis2.service;

import com.example.demo.redis2.DistributedLockCallback;
import com.example.demo.redis2.DistributedLockTemplate;
import com.example.demo.redis2.dao.TestEntityRepository;
import com.example.demo.redis2.entity.TestEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.transaction.Transactional;

/**
 * Created by LiaoKe on 2017/5/22.
 */
@Service
public class AsyncService {

    @Resource
    TestEntityRepository ts;


    @Resource
    DistributedLockTemplate distributedLockTemplate;

    /**
     * lock
     */
    @Async
    @Transactional
    public void addAsync () {
        distributedLockTemplate.lock(new DistributedLockCallback<Object>(){
            @Override
            public Object process() {
                add();
                return null;
            }

            @Override
            public Object fallback() {
                reduce();
                return null;
            }

            @Override
            public String getLockName() {
                return "MyLock";
            }
        });
    }

    /**
     * Unlocked
     */
    @Async
    public void addNoAsync(){
        add();
    }

    /**
     * Test async methods
     * Without distributed locks
     * num numbers will be confusing
     */
    @Async
    public void add(){
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }
        if(ts.findAll().size()==0){
            TestEntity t = new TestEntity ();
            t.setNum (1);
            ts.saveAndFlush(t);
        }else{
            TestEntity dbt = ts.findAll().get(0);
            dbt.setNum (dbt.getNum () + 1);
            ts.saveAndFlush(dbt);
        }
    }

    /**
     * fallback
     */
    @Async
    private void reduce(){
        if(ts.findAll().size()==0){
            TestEntity t = new TestEntity ();
            t.setNum (1);
            ts.saveAndFlush(t);
        }else{
            TestEntity dbt = ts.findAll().get(0);
            dbt.setNum (dbt.getNum () - 1);
            ts.saveAndFlush(dbt);
        }
    }


}
 

 Fallback implements the rollback of business timeouts, the number is reduced by 1, and the @Transactional transaction annotation is added to the method to prevent exceptions from occurring in fallback, but the number is missing +1

 

Due to the default lock settings we are using, the timeout is 5 seconds, to simulate the timeout I have the thread paused for 6 seconds in the add() method. Let's look at the effect.

 

package com.example.demo.redis2;

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

import javax.persistence.Transient;
import java.util.concurrent.TimeUnit;

/**
 * Single Instance mode distributed lock template
 *
 * @author lk
 */
public class SingleDistributedLockTemplate implements DistributedLockTemplate {
    private static final long     DEFAULT_TIMEOUT   = 5;
    private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;

    private RedissonClient    redisson;

    public SingleDistributedLockTemplate() {
    }

    public SingleDistributedLockTemplate(RedissonClient redisson) {
        this.redisson = redisson;
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback) {
        return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) {
        RLock lock = null;
        try {
            System.out.println("Acquire lock......");
            lock = redisson.getLock(callback.getLockName());
            lock.lock(leaseTime, timeUnit);
            T d = callback.process();
            return d;
        } finally {
            if (lock != null) {
                if(!lock.isHeldByCurrentThread()){
                    System.out.println("Automatically release the lock after timeout......");
                    callback.fallback();
                }else{
                    System.out.println("Release lock......");
                    lock.unlock();
                }
            }

        }
    }

    public void setRedisson(RedissonClient redisson) {
        this.redisson = redisson;
    }

}

 

 

The same url (see the previous article) access result is as follows . It can be

 

 seen that the database is not +1, and the fallback strategy is successful.

 

Things must be added, let's simulate fallback failure now

 

  /**
     * fallback
     */
    @Async
    private void reduce(){
        int i = 5 /0 ;
        if(ts.findAll().size()==0){
            TestEntity t = new TestEntity ();
            t.setNum (1);
            ts.saveAndFlush(t);
        }else{
            TestEntity dbt = ts.findAll().get(0);
            dbt.setNum (dbt.getNum () - 1);
            ts.saveAndFlush(dbt);
        }
        
    }

 Added a runtimeException

 

and get rid of @Trabsactional

Let's see the results



 

 



 However, the data in the database has increased, and the display is not acceptable.

Now we add transaction annotations


It can be seen that under the transaction, even the first +1 is not submitted, our transaction strategy is successful

 

Attach the demo below
 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326685057&siteId=291194637