preface

I believe that partners are using distributed services, it must be around the distributed service data concurrent update problem!

Java locks such as Synchronize and ReentrantLock are common in a single system. How does a distributed system handle them?

Using distributed locks, of course.

If you don’t know what distributed lock is, you should check out ishigari’s surprise class or search the relevant information on the Internet.

When Redis is used as a distributed lock, Redisson is currently the most used framework.

Of course, Redisson is not only used as a lock, but also has many other functions, which you can check out the official documentation and try out for yourself.

Take notes about Redisson below! Any mistakes are welcome. 🙏 🏻

Environment configuration

  • Pseudo cluster built in local environment:

  • Redisson 3.15.6

Different versions may be different, but the core idea will not change much, if the change is great, I hope you can leave a message.

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.6</version>
</dependency>
Copy the code
  • Project preparation

A simple Maven project requires only a Main method.

Reentrant lock lock

At lock.lock() breakpoint, as source entry.

Default lock, no arguments passed. But leaseTime is set to -1. LeaseTime means the time to lock.

The rest of the way through.

Before the tryAcquire method is called, there is an additional argument threadId, which is the id of the current thread and a positive long.

Asynchronous lock

Go straight to the tryAcquireAsync asynchronous locking method.

We already said that leaseTime is -1, so this is going to go into the method below.

So far the parameters are clear:

  1. WaitTime: – 1;
  2. InternalLockLeaseTime: use the default time 30000 milliseconds.
  3. Timeunit. MILLISECONDS: MILLISECONDS;
  4. ThreadId: threadId;
  5. RedisCommands. EVAL_LONG: eval.

The documentation for Redis eval can be read: Redis. IO /commands/ev…

Locking logic

The real lock, in fact, is such a lua script.

Lua script parameters:

  1. KEYS[1] : getRawName(), lock key, such as anyLock;
  2. ARGV[1] : ununit.tomillis (leaseTime), lock milliseconds, such as 30,000;
  3. ARGV[2] : getLockName(threadId), which is a string concatenated with the UUID and threadId, such as 931573de-903e-42fd-baa7-428ebb7eda80:1.

Because lua scripts are used, atomicity of this section of Lua script is guaranteed.

First locking analysis:

  1. The exists command determines whether redis anyLock exists.
  2. If no, run hincrby to create anyLock data.
  3. Set an expiration time for anyLock.

The data format in Redis after locking is:

The Hash data structure for Redis can be read: Redis. IO /topics/data…

AnyLock has a k-V structure attached to it:

"anyLock": {"f400aad5-4b1f-4246-a81e-80c2717c3afb:1":"1"
}
Copy the code

Execute the script

The next step is to make the request to execute the Lua script, the only thing that needs to be noted is that there is a hash slot route.

This code is called in the CommandAsyncService#evalWriteAsync method to get a NodeSource.

Of course the NodeSource contains only one slot (hash slot).

The slot value is calculated using the CRC16 algorithm for the locked key.

// MAX_SLOT defaults to 16384
int result = CRC16.crc16(key.getBytes()) % MAX_SLOT;
Copy the code

What’s the point of calculating a slot?

Keep tracking!

BaseRedisBatchExecutor#addBatchCommandData gets solt from source, and then gets MasterSlaveEntry.

Obtain the node corresponding to the Redis key.

reentrant

Since it’s a reentrant lock, this is reentrant support, so let’s see how reentrant is guaranteed.

  1. The exists command determines whether the redis key field exists.
  2. If yes, run the hincrby command to increment the value corresponding to the key field.
  3. Set the expiration time for the current Redis key.

Lock mutex

Two cases have been verified above:

  1. Redis key does not exist;
  2. Redis key and key field exist.

The remaining case is where the key exists, but the field does not.

The key field contains UUID:ThreadId, which indicates that the current thread is not locked. The remaining time of the current lock is returned.

conclusion

This paper mainly introduces Redisson reentrant lock lock, lock reentrant, lock mutually exclusive logic.

The core focus is on Lua scripts. You also need to understand the Redis Hash data structure.

Also, remember that if the lock time is not specified, 30s is used by default.

Finally, a diagram illustrates the locking logic in this article.

Related to recommend

  • How do Spring dynamic proxies address circular dependencies? Why use level 3 caching?
  • How does Spring address loop dependencies?
  • How did you resolve the Spring self-invocation transaction failure?