Very simple introduction

  • Redisson was used as a distributed lock implementation in the project, so I thought I would look at its source code and record it.

There is no typographical analysis process

  • Without further ado, let’s do the above
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.123.</version>
</dependency>
Copy the code

  • Above is the basic use of redisson.
  • Here are a few key points about using distributed locks, so that you can read the Redisson source code to see how it is implemented based on the ideas presented.
    • The execution of lock and expiration commands requires atomicity (using lua script commands);
    • Solution to lock expiration when business code is not finished (use timer to renew the current lock expiration time);

  • The following is an analysis of the important method source code in Redisson

  • There are two lock methods in RedissonLock, one with no parameters and one with parameters. The difference will be described later. The lock(Long leaseTime, TimeUnit Unit, Boolean Interruptibly) method is entered through debugging

  • TryLockInnerAsync (long leaseTime, TimeUnit Unit, long threadId, RedisStrictCommand command

  • Here a look at the middle of a string of Lua script command is the focus, because redis single thread execution reasons, the use of Lua script command has atomicity, meet one of the conditions of distributed lock.

  • Call (‘hincrby’, KEYS[1], ARGV[2], 1) redisson stores locks in a hash structure. According to the three parameters above:
    • KEYS[1] : redisson_key
    • ARGV[1] : 30000 (key expiration time)
    • ARGV[2] : 92821D82-D541-4a1F-9F9E-AE908f0b9b38:1 (equivalent to redis-hashkey) ARGV[2] : 92821D82-D541-4a1F-9F9e-AE908f0b9b38:1
  • Its execution flow is as follows:
    • Check whether the key exists first. If the key does not exist, the key and the hashValue of the hashKey are assigned a value of 1, the expiration time is set to 30 seconds, and null is returned.
    • If the key exists, check whether the key and hashKey also exist. If the key and hashKey exist, the hashValue of the key and hashKey is +1 and the expiration time is set to 30 seconds, and then null is returned.
    • If none of the above conditions are met, return the remaining expiration time of the key.

  • This is basically the end of the lock operation. Use the tool to see the values cached in Redis (see figure above).

  • Continuing to debug will enter the renewExpiration() method. An important point in redisson’s source code design is the watchdog mechanism, which prevents locks from expending while still in use. Source watchdog is a timed task design, timed time is internalLockLeaseTime/3(30,000/3 so the default is 10 seconds) ms.
  • Click here to enter the newTimeout() method and find that the timer here uses the Netty-based HashedWheelTimer to realize the timing mechanism HashedWheelTimer in NetTY.

  • The RenewationAsync (Long threadId) method is a Lua script to renew the expiration time of locks:
    • Check whether the key and hashkey exist, reset their expiration time to 30 seconds if they do, and return true.
    • Otherwise return false.

  • Finally, if the lock fails, then TTL returns the remaining expiration time of the lock, and the subscribe(threadId) method circled in the figure above is applied.
  • Here the entire distributed locking of the two main process analysis completed.

  • Note: It should be mentioned here that if you don’t want to use Redisson’s watchdog mechanism, use the lock(Long leaseTime, TimeUnit Unit) method with parameters. If leaseTime is not -1, only the lua script lock method will be executed, not the subsequent timer method.

  • Before looking at the lock method method source. Continuing to debug the unlock method will bring up the unlockInnerAsync(Long threadId) method.

  • Parameter analysis of the figure above:
    • KEYS[1] : redisson_key (equivalent to redis_key)
    • KEYS[2] : redisson_lock__channel:{redisson_key}
    • ARGV[1] : 0 (body of publish notification)
    • ARGV[2] : 30000 (equivalent to key expiration time)
    • ARGV[3] : 92821D82-D541-4a1F-9F9E-AE908f0b9b38:1 (equivalent to redis_hashKey) ARGV[3] : 92821D82-D541-4a1F-9F9e-AE908f0b9b38:1
  • Its execution process:
    • First check the key and hashKey and return null if they do not exist.
    • If it exists, perform -1 on its hashValue and get the value.
    • If greater than 0, the key is reassigned an expiration time of 30 seconds.
    • If it is 0, the key is removed and the notification is published.
  • There are two important points here:
    • The hash structure is used here to hold the lock primarily for reentrant lock implementation. In the previous lua command, the hashValue of the key and hashkey is +1 if the data is found by the key and hashkey. In this command, the hashValue of the key and hashkey is -1.
    • Publish at the end of the lua unlock script notifes other client threads that subscribe(long threadId) after failing to subscribe during the lock phase.

  • Then continue debugging to enter the cancelcontext Renewal(Long threadId) method, which removes scheduled tasks in the CONTEXT _RENEWAL_map set and terminates the watchdog timer mechanism.
  • Here redisson unlock method source code is also basic analysis completed.
  • Note: If you are using the lock () method with no parameters, that have a watchdog timer mechanism to protect the thread of execution, so you must call unlock () to unlock (no matter use which kinds of locking method, must call the unlock method), otherwise the watchdog timer will always give lock refills expiration time, as long as the program has been performed then lock basic don’t expire, This is equivalent to creating a deadlock.

I want to end

  • To be honest, Redisson’s source code is relatively easy to read compared to other frameworks.
  • Learn with an open mind and make progress together