Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize distributed Lock by integrating SpringBoot with Redisson

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article will explain in detail how SpringBoot integrates Redisson to achieve distributed locks. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

Redisson is a Java resident memory data grid (In-Memory Data Grid) based on redis. Making full use of a series of advantages provided by Redis key-value database, a series of common tool classes with distributed characteristics are provided for users based on the interfaces commonly used in Java utility kit. As a result, the toolkit, which was originally used to coordinate single-machine multithreaded concurrent programs, obtains the ability to coordinate distributed multi-threaded concurrent systems, which greatly reduces the difficulty of designing and developing large-scale distributed systems. At the same time, combined with the unique distributed services, it further simplifies the cooperation between programs in the distributed environment.

First, add dependency org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis org.redisson redisson-spring-boot-starter 3.13.6 II. Redis configuration file server: port: 8000 spring: redis: host: localhost port: 6379 password: null database: 1 timeout: 30000 III. Create a new configuration class @ Configurationpublic class MyRedissonConfig {@ Value ("${spring.redis.host}") String redisHost @ Value ("${spring.redis.port}") String redisPort; @ Value ("${spring.redis.password}") String redisPassword; @ Value ("${spring.redis.timeout}") Integer redisTimeout; / * Redisson configuration * @ return * / @ Bean RedissonClient redissonClient () {/ / 1, create configuration Config config = new Config () RedisHost = redisHost.startsWith ("redis://")? RedisHost: "redis://" + redisHost; SingleServerConfig serverConfig = config.useSingleServer () .setAddress (redisHost + ":" + redisPort) .setTimeout (redisTimeout); if (StringUtils.isNotBlank (redisPassword)) {serverConfig.setPassword (redisPassword);} return Redisson.create (config);}} / standalone RedissonClient redisson = Redisson.create () Config config = new Config (); config.useSingleServer (). SetAddress ("myredisserver:6379"); RedissonClient redisson = Redisson.create (config); / / Master / Slave Config config = new Config (); config.useMasterSlaveServers () .setMasterAddress ("127.0.0.1pur6379") .addSlaveAddress ("127.0.0.1myredisserver:6379 6389", "127.0.0.16332") .addSlaveAddress ("127.0.0.1pur6399") .addSlaveAddress ("127.0.0.1pur6399") RedissonClient redisson = Redisson.create (config); / / Sentinel Config config = new Config (); config.useSentinelServers () .setMasterName ("mymaster") .addSentinelAddress ("127.0.0.1Vl26389", "127.0.0.1RV 26379") .addSentinelAddress ("127.0.0.1RV 26319"); RedissonClient redisson = Redisson.create (config); / / Cluster Config config = new Config () Config.useClusterServers () .setScanInterval (2000) / / cluster state scan interval in milliseconds .addNodeAddress ("127.0.0.1V7000", "127.0.0.1V7001") .addNodeAddress ("127.0.0.1ve7002"); RedissonClient redisson = Redisson.create (config); fourth, the lock can be reentered using distributed locks

The Redisson distributed reentrant lock RLock object based on Redis implements the java.util.concurrent.locks.Lock interface.

@ RequestMapping ("/ redisson") public String testRedisson () {/ / acquire the distributed lock, as long as the lock name is the same, it is the same lock RLock lock = redissonClient.getLock ("lock"); / / lock (blocking wait). The default expiration time is indefinite lock.lock () Try {/ / if the business execution is too long, Redisson will automatically renew the lock Thread.sleep (1000); System.out.println ("lock successfully, execute business logic");} catch (InterruptedException e) {e.printStackTrace () } finally {/ / unlock, if the business execution is completed, it will not renew lock.unlock ();} return "Hello Redisson!";}

If the node that gets the distributed lock is down, and the lock happens to be locked, the lock will be locked. In order to avoid this situation, the lock will set an expiration time. There is also a problem. A thread gets the lock and sets a 30s timeout. After 30s, the thread has not finished executing, and the lock timeout is released, which will lead to the problem. Redisson gives its own answer, which is the automatic extension mechanism of watch dog.

Redisson provides a watchdog to monitor the lock, which is used to extend the validity of the lock before the Redisson instance is closed, that is, if a thread that has acquired the lock has not completed the logic, then the watchdog will help the thread to extend the lock timeout, and the lock will not be released because of the timeout.

By default, the watchdog's renewal time is 30s, which can also be specified by modifying the Config.lockWatchdogTimeout.

In addition, Redisson also provides a locking method that can specify the leaseTime parameter to specify the locking time. After this time, the lock will be unlocked automatically and the validity period of the lock will not be extended.

In the renewExpiration () method of the RedissonLock class, a scheduled task is started to renew the lock every 30 seconds. If the application fails during the execution of the business, it will not be renewed automatically, and the lock will be automatically released after the expiration time.

Private void renewExpiration () {ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get (getEntryName ()); if (ee = = null) {return;} Timeout task = commandExecutor.getConnectionManager () .newTimeout (new TimerTask () {@ Override public void run (Timeout timeout) throws Exception {ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get (getEntryName () If (ent = = null) {return;} Long threadId = ent.getFirstThreadId (); if (threadId = = null) {return;} RFuture future = renewExpirationAsync (threadId) Future.onComplete ((res, e)-> {if (e! = null) {log.error ("Can't update lock" + getName () + "expiration", e); return } if (res) {/ / reschedule itself renewExpiration ();}});}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); ee.setTimeout (task);}

In addition, Redisson also provides a parameter of leaseTime to specify the locking time. After this time, the lock will be unlocked automatically.

/ / automatically unlock lock.lock (10, TimeUnit.SECONDS) 10 seconds after locking / / No need to call the unlock method to manually unlock lock.lock (10, TimeUnit.SECONDS); / / attempt to lock, wait up to 100 seconds, and automatically unlock boolean res = lock.tryLock (100,10, TimeUnit.SECONDS) 10 seconds after locking

If the timeout of the lock is specified, the underlying layer directly calls the lua script to occupy the lock. If the leaseTime is exceeded and the business logic has not been completed, the lock is released directly, so when you specify the leaseTime, make the leaseTime greater than the business execution time. The tryLockInnerAsync () method of the RedissonLock class

RFuture tryLockInnerAsync (long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand command) {internalLockLeaseTime = unit.toMillis (leaseTime); return evalWriteAsync (getName (), LongCodec.INSTANCE, command, "if (redis.call ('exists', KEYS [1]) = = 0) then" + "redis.call (' hincrby', KEYS [1], ARGV [2], 1) "+" redis.call ('pexpire', KEYS [1], ARGV [1]); "+" return nil; "+" end "+" if (redis.call ('hexists', KEYS [1], ARGV [2]) = = 1) then "+" redis.call (' hincrby', KEYS [1], ARGV [2], 1); "+" redis.call ('pexpire', KEYS [1], ARGV [1]); "+" return nil "+" end; "+" return redis.call ('pttl', KEYS [1]); ", Collections.singletonList (getName ()), internalLockLeaseTime, getLockName (threadId);} read-write lock

Distributed reentrant read-write locks allow multiple read locks and one write lock to be locked at the same time. In the read-write lock, read sharing, read-write mutual exclusion, and write-write mutual exclusion.

RReadWriteLock rwlock = redisson.getReadWriteLock ("anyRWLock"); / / the most common usage is rwlock.readLock () .lock (); / / or rwlock.writeLock () .lock ()

Read-write lock test class, when accessing the write interface, the read interface will be blocked.

@ RestControllerpublic class TestController {@ Autowired RedissonClient redissonClient; @ Autowired StringRedisTemplate redisTemplate; @ RequestMapping ("/ write") public String write () {RReadWriteLock readWriteLock = redissonClient.getReadWriteLock ("wr-lock"); RLock writeLock = readWriteLock.writeLock (); String s = UUID.randomUUID (). ToString (); writeLock.lock (); try {redisTemplate.opsForValue (). Set ("wr-lock-key", s) Thread.sleep (10000);} catch (InterruptedException e) {e.printStackTrace ();} finally {writeLock.unlock ();} return s;} @ RequestMapping ("/ read") public String read () {RReadWriteLock readWriteLock = redissonClient.getReadWriteLock ("wr-lock"); RLock readLock = readWriteLock.readLock (); String s = "" ReadLock.lock (); try {s = redisTemplate.opsForValue (). Get ("wr-lock-key");} finally {readLock.unlock ();} return s;}} semaphore (Semaphore)

Redis-based Redisson distributed semaphore (Semaphore) Java object RSemaphore adopts a similar interface and usage to java.util.concurrent.Semaphore

With regard to the use of semaphores, you can imagine this scene where there are three parking spaces, and when the three parking spaces are full, the rest of the cars will not stop. Can compare the parking space to the signal, now there are three signals, stop the car once, use a signal, the car leaves is to release a signal.

We use Redisson to demonstrate the above parking space scene.

First define a way to occupy a parking space:

/ * parking, occupying parking spaces * total 3 parking spaces * / @ ResponseBody@RequestMapping ("park") public String park () throws InterruptedException {/ / get semaphore (parking) RSemaphore park = redisson.getSemaphore ("park"); / / get a signal (parking space) park.acquire (); return "OK";}

Define another way to leave the parking space:

/ * * release parking spaces * a total of 3 parking spaces * / @ ResponseBody@RequestMapping ("leave") public String leave () throws InterruptedException {/ / get semaphore (parking) RSemaphore park = redisson.getSemaphore ("park"); / / release a signal (parking space) park.release (); return "OK";}

For simplicity, I used the Redis client to add a key: "park" with a value equal to 3, indicating that the semaphore is park, with a total of three values.

Then use postman to send a park request to occupy a parking space.

Then check the value of park in the redis client and find that it has been changed to 2. If you continue to call it twice, you will find that the value of park is equal to 0. when you call it for the fourth time, you will find that the request has been waiting, indicating that there is not enough parking space. If you want to avoid blocking, you can use tryAcquire or tryAcquireAsync.

We then call the method to leave the parking space, and the value of park changes to 1, indicating that there is one parking space left.

Note: if you perform the release semaphore operation repeatedly, the remaining semaphore will grow all the time, instead of capping it after 3.

Lockout (CountDownLatch)

The role of CountDownLatch: a thread waits for other threads to finish execution before continuing to execute itself.

RCountDownLatch latch = redisson.getCountDownLatch ("anyCountDownLatch"); latch.trySetCount (1); latch.await (); / / in other threads or other JVM RCountDownLatch latch = redisson.getCountDownLatch ("anyCountDownLatch"); latch.countDown ()

Add a test method to the TestController, and when you access the close interface, call the await () method to enter the blocking state, and the close interface will not return until there are three visits to the release interface.

@ RequestMapping ("/ close") public String close () throws InterruptedException {RCountDownLatch close = redissonClient.getCountDownLatch ("close"); close.trySetCount (3); close.await (); return "close";} @ RequestMapping ("/ release") public String release () {RCountDownLatch close = redissonClient.getCountDownLatch ("close"); close.countDown (); return "release" } this is the end of the article on "how SpringBoot integrates Redisson to achieve distributed locks". I hope the above content can be helpful to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report