Redisson initial

Recently, I have been doing old-age technology without any upgrades, and I am almost numb. What should I say? I must take action!~ Come on, let’s increase our internal strength together.

The strongest implementation of distributed locks:Redisson

1. Concept

Before introducing, we need to know what this Redisson is? Is it Redis’s son? (I I just thought so once, haha!) This is indeed the case –>See explanation below

First somethingProfessional pointExplanation:

  1. RedissonisIn-momery data Grid (based on a memory-resident grid) (A buffering technology for distributed distributionIt’s great!)Redisjava

  2. Communication is based onNetty, oriented to enterprise-level development

  3. Provide a series of distributedjavacommon objects and distributed services

    Vernacular:

    ​ a. It is a distributed tool that encapsulates many common distributed functions.

    ​ b. Simplify developer development and focus more on business development instead of spending most of the timeRedison the above

    c. Avoid distributed wheel-making

2. Distributed lock

Let me talk about this stuff too, shall I? It is a necessity for concurrent business; as the saying goes: Whether concurrency is good or not depends on how well this lock is managed!

What is thereexample? (Yes! Wait~) We will use Redis to write a simple distributed lock:~

Write the simplestRedis distributed lock: using SpringDataRedisRedisTemplate

setIfAbsent:setNx+expire

2.1 Lock operation/unlock operation

// 加锁
public Boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
    
    
    return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
}
// 解锁,防止删错别人的锁,以uuid为value校验是否自己的锁
public void unlock(String lockName, String uuid) {
    
    
    if(uuid.equals(redisTemplate.opsForValue().get(lockName)){
    
            
        redisTemplate.opsForValue().del(lockName);    
    }
}
// 结构
if(tryLock){
    
    
    // todo  
}finally{
    
    
    unlock;
}       

The above code completes the lock operation, but we will find that it cannot guaranteeatomicity. Once the amount of concurrency is high, it will be a big deal! ~

How to solve it? How to ensure atomicity? How do I know! (Haha! Let’s read on and we will understand) We introduce the built-in language of Redis:lua,

2.2 For the purposeluaScreenplay

We uselua to encapsulate the operation into a lua script, through Redis< a i=4>/evalevalsha

luaScript code:

Script name:lockDel.lua

if redis.call('get', KEYS[1]) == ARGV[1] 
    then 
 -- 执行删除操作
        return redis.call('del', KEYS[1]) 
    else 
 -- 不成功,返回0
        return 0 
end

RemovaljavaYogo:

// 解锁脚本
DefaultRedisScript<Object> unlockScript = new DefaultRedisScript();
unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lockDel.lua")));

// 执行lua脚本解锁
redisTemplate.execute(unlockScript, Collections.singletonList(keyName), value);

is written here, we have also addedlua, which can also ensure atomicity! But look at it, is there anything missing? (Haha, are you thinking in your heart? , I see a hammer!There’s nothing wrong with it)

Hmm, let me remind you - follow the above idea: What if a thread holds the lock multiple times? Good guy Is there a problem? How to solve it? ( Have you thought about it? Just look down!Haha)

2.3 Reentrant lock

How to ensure re-entry?

First of all: We should understand the reentrant core: it is allowed for the same thread to acquire the same lock multiple times, and no deadlock will occur

Hmm~ I don’t understand the truth very well. We use Synchronized’s biased lock to understand its implementation idea< /span>

  1. synchronizedReentrancy is implemented at theJVM level.javaThe object headerMARK word contains the thread ID and counter to perform operations on the thread. Reentrant judgment to avoid each timeCAS
  2. When a thread accesses the synchronized block and acquires the lock, it will be in the object header and stack frames 'slock record stores biased threadsID
  3. In this way, when this thread ID enters and exits the synchronization block later, there is no needCAS to lock and unlock
  4. You only need to check whether the MARK WORD object header stores a bias lock pointing to the current thread.
    • Test successful: thread acquires lock
    • Test failed: test againMARK WORDbias lockflagwhetherSet to 1
      • 0: LostCASConflict
      • 1:CASPoint the object head bias lock to the current thread
  5. There is also a counter: when the same thread enters, it will increase by 1, and when it leaves, it will decrease by 1 until it reaches 0Release

According to the above idea: (We modify the script to be implementedlua)

1. Prepare formal parameters (conditions for execution)
需要存储锁名称lockName
该锁线程ID
对应线程进入的次数: count
2. Lock (each time a thread acquires a lock, it determines whether the lock already exists)
不存在:
	设置hash的Key为线程ID,value=1
	设置expireTime(过期时间)
	返回获取锁,成功为true
存在:
	继续判断是否存在当前线程ID的hash key
	存在:
		线程key的value+1,重入次数加1(count++),设置expireTime
	不存在:
		返回加锁失败
3. Unlock —> Every time a thread comes to unlock, determine whether the lock already exists
存在:
	是否有该线程的ID的hash key,有则减1,没有返回解锁失败
	减1:
		判断剩余count为不为0
		=0: 不再需要这把锁,执行del命令删除
1. Storage structure:

Redis uses HASH structure

Lock name: lock_name1`

key: thread_id (unique key, thread ID)

value:count` (计数器)

hset  lock_name1 thread_id  1

hget  lock_name1
2. Counter addition and subtraction

When the same thread acquires the same lock, we need to add or subtract the counter count of the corresponding thread.

How to determine whether a Redis key exists?

We can use exists/hexists to determine whether a hash key exists.

hset  lock_name1  thread_id  1 
exists/exists  lock_name1

Hash auto-increment command:

hincrby   lock_name1  thread_id  1
3. Determination of unlocking

When a lock is no longer needed, every time it is unlocked, the count decreases by 1 until it reaches 0, and the deletion is performed.

finalLUAdaigo

Locklock.lua

local key = KEYS[1];
local threadId = ARGV[1];
local releaseTime = ARGV[2];

-- lockname不存在
if(redis.call('exists', key) == 0) then
    redis.call('hset', key, threadId, '1');
    redis.call('expire', key, releaseTime);
    return 1;
end;

-- 当前线程已id存在
if(redis.call('hexists', key, threadId) == 1) then
    redis.call('hincrby', key, threadId, '1');
    redis.call('expire', key, releaseTime);
    return 1;
end;
return 0;

Unlockunlock.lua

local key = KEYS[1];
local threadId = ARGV[1];

-- lockname、threadId不存在
if (redis.call('hexists', key, threadId) == 0) then
    return nil;
end;

-- 计数器-1
local count = redis.call('hincrby', key, threadId, -1);

-- 删除lock
if (count == 0) then
    redis.call('del', key);
    return nil;
end;

RedisLock.java: Implement distributed locks

Function implementation: Mutual exclusion, reentrancy, anti-deadlock

/**
 * @description 原生redis实现分布式锁
 **/
@Getter
@Setter
public class RedisLock {
    
    

    private RedisTemplate redisTemplate;
    private DefaultRedisScript<Long> lockScript;
    private DefaultRedisScript<Object> unlockScript;

    public RedisLock(RedisTemplate redisTemplate) {
    
    
        this.redisTemplate = redisTemplate;
        // 加载加锁的脚本
        lockScript = new DefaultRedisScript<>();
        this.lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lock.lua")));
        this.lockScript.setResultType(Long.class);
        // 加载释放锁的脚本
        unlockScript = new DefaultRedisScript<>();
        this.unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("unlock.lua")));
    }

    /**
     * 获取锁
     */
    public String tryLock(String lockName, long releaseTime) {
    
    
        // 存入的线程信息的前缀
        String key = UUID.randomUUID().toString();

        // 执行脚本
        Long result = (Long) redisTemplate.execute(
                lockScript,
                Collections.singletonList(lockName),
                key + Thread.currentThread().getId(),
                releaseTime);

        if (result != null && result.intValue() == 1) {
    
    
            return key;
        } else {
    
    
            return null;
        }
    }

    /**
     * 解锁
     * @param lockName
     * @param key
     */
    public void unlock(String lockName, String key) {
    
    
        redisTemplate.execute(unlockScript,
                Collections.singletonList(lockName),
                key + Thread.currentThread().getId()
                );
    }
}

The above code has implemented most of the functions and can handle general scenarios calmly, but (hahaha, are you most afraid of failure?)

针对Special scene:

1. IfA process acquires the lock because the business operation takes too long , the lock is released, but the business is still being executed. At this moment, process B can obtain the lock normally and perform business operations. At this time, processes A and B will appearProblems with shared resources

2. If the Redis responsible for storing this distributed lock goes down, the lock will be in Locked state, which will result in locked state

In this case, we need to useLock renewal

Lock renewal: Extend the lockReleaseTime until the desired business results are completed. The essence is tocontinuously extend the lock Expiration date

But, regarding the processing of renewals, performance such as the maximum waiting time for locks, invalid lock applications, and the failed retry mechanism, we have been writing about it for a long time! ~Push now=—>Give up hahaha!!

No, no, no, no, no, no, no, no, no, no, no, no, no, no, here we are going to introduce our protagonist today:Reidsson!! (solve all the above problems)

4.RedissonDistributed lock

The soldiers and horses have not moved, but the food and grass go first

Let’s see how to use it~ shall we?

4.1Introducing dependencies

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

<!-- 另一种Spring集成starter,本章未使用 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.6</version>
</dependency>

4.2 Configuration

@Configuration
public class RedissionConfig {
    
    
    @Value("${spring.redis.host}")
    private String redisHost;

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

    private int port = 6379;

    @Bean
    public RedissonClient getRedisson() {
    
    
        Config config = new Config();
        config.useSingleServer().
                setAddress("redis://" + redisHost + ":" + port).
                setPassword(password);
        config.setCodec(new JsonJacksonCodec());
        return Redisson.create(config);
    }
}

4.3 Enable distributed locks

Concise and clear, only one is neededRLock, as shown in the following code

@Resource
private RedissonClient redissonClient;

RLock rLock = redissonClient.getLock(lockName);
try {
    
    
    boolean isLocked = rLock.tryLock(expireTime, TimeUnit.MILLISECONDS);
    if (isLocked) {
    
    
        // TODO
                }
    } catch (Exception e) {
    
    
            rLock.unlock();
    }

4.4RLock

Still updating…

Guess you like

Origin blog.csdn.net/weixin_43475992/article/details/134204095