First of all about the introduction of Redisson, here will not carry, paste github original address:
- An overview of the
Since I have only briefly used Redisson’s distributed lock feature here, only a simple use of the lock is recorded here.
8. Distributed locks and synchronizers
This lock is a reentrant lock
8.1. Reentrant Lock
Based on Redis Redisson distributed reentrant Lock RLock Java object implements the Java. Util. Concurrent. The locks. Lock interface. It also provides Async, Reactive and RxJava2 standard interfaces.
RLock lock = redisson.getLock("anyLock");
// The most common way to use it
lock.lock();
Copy the code
As you know, if the Redisson node that stores the distributed lock goes down, and the lock happens to be in the locked state, the lock will be locked. To prevent this from happening, Redisson internally provides a lock watchdog that continuously extends the lifetime of the lock before the Redisson instance is closed. By default, the watchdog check lock timeout time is 30 seconds, but can be by modified Config. LockWatchdogTimeout to specify separately.
In addition, Redisson provides the leaseTime parameter to specify the lock time through the lock method. After this time, the lock unlocks automatically.
// It will be unlocked in 10 seconds
// You do not need to call the unlock method
lock.lock(10, TimeUnit.SECONDS);
// Try locking. Wait 100 seconds at most. After locking, it will be unlocked automatically within 10 seconds
boolean res = lock.tryLock(100.10, TimeUnit.SECONDS);
if (res) {
try{... }finally{ lock.unlock(); }}Copy the code
Redisson also provides a related method for asynchronous execution of distributed locks:
RLock lock = redisson.getLock("anyLock");
lock.lockAsync();
lock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = lock.tryLockAsync(100.10, TimeUnit.SECONDS);
Copy the code
The RLock object fully conforms to the Java Lock specification. That is only a process of lock can unlock, other processes to unlock will throw IllegalMonitorStateException errors. However, if you need other processes to unlock, use distributed Semaphore objects.
First, SpringBoot needs to introduce the redisson dependency:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>
Copy the code
Test the above version, with springBoot: 2.2.5.RELEASE, normal use.
The configuration file of the standalone Redis is the same as the original SpringBoot integrated Redis.
Password: huauN@2021 database: 0 host: 192.168.104.64 port: 6379 #cluster: #max-redirects: 3 # Retrieve maximum redirection times #nodes: # -192.168.104.101:6379 lettuce: pool: max-active: 1024 # Maximum number of connections in a pool (default: 8, -1 indicates unlimited) max-wait: The default value is -1, indicating that JedisConnectionException will never timeout. Max-idle: 10 min-idle: 5Copy the code
Redis configuration mapping class RedisConfigProperties. Java
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.List; /** * Redis configuration mapping class ** @author linmengmeng * @date 2021-03-11 **/ @Component @configurationProperties (prefix = "spring.redis") public class RedisConfigProperties { private Integer timeout; private Integer database; private Integer port; private String host; private String password; private cluster cluster; public static class cluster { private List<String> nodes; public List<String> getNodes() { return nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } } public Integer getTimeout() { return timeout; } public void setTimeout(Integer timeout) { this.timeout = timeout; } public Integer getDatabase() { return database; } public void setDatabase(Integer database) { this.database = database; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public RedisConfigProperties.cluster getCluster() { return cluster; } public void setCluster(RedisConfigProperties.cluster cluster) { this.cluster = cluster; }}Copy the code
Add the auto-assembly class: redissonconfig.java
import gc.cnnvd.config.properties.RedisConfigProperties;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/ * * *@author linmengmeng
* @author The 2021-08-30 * /
@Configuration
public class RedissonConfig {
@Autowired
private RedisConfigProperties redisConfigProperties;
/** * redis://host:port */
private static final String REDIS_ADDRESS = "redis://%s:%s";
// /**
// * Cluster mode - Add redisson's bean
// * @return
/ / * /
// @Bean
// public Redisson redisson() {
// // Redisson is 3.5. Redis :// must be added before the IP address of the cluster. Otherwise, an error will be reported
// List
clusterNodes = new ArrayList<>();
// for (int i = 0; i < redisConfigProperties.getCluster().getNodes().size(); i++) {
// clusterNodes.add("redis://" + redisConfigProperties.getCluster().getNodes().get(i));
/ /}
// Config config = new Config();
// ClusterServersConfig clusterServersConfig = config.useClusterServers()
// .addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));
// clusterServersConfig.setPassword(redisConfigProperties.getPassword()); // Set the password
// return (Redisson) Redisson.create(config);
/ /}
/** * Redisson single-player mode *@return* /
@Bean
public Redisson RedissonConfig(){
Config config = new Config();
// config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(redisConfigProperties.getDatabase());
config.useSingleServer().setAddress(String.format(REDIS_ADDRESS, redisConfigProperties.getHost(), redisConfigProperties.getPort()))
.setDatabase(redisConfigProperties.getDatabase())
.setPassword(redisConfigProperties.getPassword());// No password can be set
return(Redisson) Redisson.create(config); }}Copy the code
Create a test interface to test distributed locks:
package gc.cnnvd; import gc.cnnvd.framework.common.api.ApiResult; import lombok.extern.slf4j.Slf4j; import org.redisson.Redisson; import org.redisson.api.RLock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * @Auther linmengmeng * @Date 2021-09-01 15:37 */ @Slf4j @RestController @RequestMapping("/tourist") public class TestRedissonLockController { private static String FORMAT_LOCKKEY = "testLockKey:%s"; @autowired private RedisTemplate RedisTemplate; @Autowired private Redisson redisson; @PostMapping("/testLock11") public ApiResult<Boolean> testLock11() { String lockKey = String.format(FORMAT_LOCKKEY, 3); log.info("-------lockKey:{}", lockKey); RLock lock = redisson.getLock(lockKey); The info (" -- -- -- -- -- -- -- after create lock isLocked 1: {} ", lock. IsLocked ()); // try { // Thread.sleep(10000); // } catch (InterruptedException e) { // e.printStackTrace(); // } Future<Boolean> res = lock.tryLockAsync(10, 30, TimeUnit.SECONDS); The info (" -- -- -- -- -- -- -- after tryLockAsync isLocked 2: {} ", lock. IsLocked ()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); {} the if (lock. IsLocked ()). The log info (" -- -- -- -- -- -- -- - isLocked after 10 seconds -- 3: {} ", lock. IsLocked ()); //throw new BusinessException(" exception after lock acquisition test "); } if (lock.isHeldByCurrentThread()){ log.info("-------isHeldByCurrentThread:{}", lock.isHeldByCurrentThread()); } boolean result = false; try { result = res.get(); log.info("-------result:" + result); if (result){ Thread.sleep(10000); if (lock.isHeldByCurrentThread()){ log.info("-------isHeldByCurrentThread:{}", lock.isHeldByCurrentThread()); } } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally { log.info("-------666666-------unlock-isLocked:{}", lock.isLocked()); If (lock. IsLocked ()) {the info (" -- -- -- -- -- -- -- 88888888 -- -- -- -- -- -- - to unlock the unlock: {} ", lock. IsLocked ()); lock.unlock(); } } log.info("res.get:{}", result); return ApiResult.ok(lock.isLocked()); } @PostMapping("/testLock12") public ApiResult<Boolean> testLock12() { String lockKey = String.format(FORMAT_LOCKKEY, 3); log.info("====================lockKey:{}", lockKey); RLock lock = redisson.getLock(lockKey); log.info("====================isLocked-1:{}", lock.isLocked()); Future<Boolean> res = lock.tryLockAsync(5, 2, TimeUnit.SECONDS); boolean locked = lock.isLocked(); log.info("====================isLocked-2:{}", locked); If (locked) {if (lock. IsHeldByCurrentThread ()) {the info (" = = = = = = = = = = = = = = = = = = = = lock, is my lock "); } else {log. The info (" = = = = = = = = = = = = = = = = = = = = locked, not my lock "); } } Boolean getLock = null; log.info("====================getLock-2:{}", getLock); return ApiResult.ok(locked); }}Copy the code
The above code looks very messy, I fumbled with the log, in order to better understand the locking and unlocking mechanism.
Add a thread sleep to the testLock11 interface to simulate the application running the lock. You can see the lock key in redis:
At first, I could not find the key. Later, I found that after the thread finished running, the lock was automatically released, and then the sleep time was prolonged, so I found the key in redis.
After the use of the code inside, the lock and lock to determine the use of the following:
- Set a unique identifier for locking
- Get the lock, hold it, and do your own business logic
Future<Boolean> res = lock.tryLockAsync(5, 5, timeunit.seconds); If (lock. IsLocked () && lock. IsHeldByCurrentThread ()) {/ / processing business logic / / unlock the if (lock) isLocked ()) {. Lock unlock (); } if (! Lock. isLocked()){log.info(" unlocked successfully "); } return; }Copy the code
Judge locked this case I used: lock isLocked () && lock. IsHeldByCurrentThread (), so that we can ensure that only part of a thread into the lock.
Unlock () : lock. IsLocked (); unlock() : lock. If the lock has been released, or another thread has acquired the lock, the current thread releasing the lock will raise an exception:
- Manual release or automatic release at expiration
2. Abnormal stepping pits
1. Error processing condition on org.springframework.boot.autoconfigure.cache.S
When the redisson dependency was first added, the original Redis configuration did not work properly and the project started with the above error. Finally, switch and instantiate CacheManager
See SpringBoot for redis caching via Cacheable annotations
# SpringBoot redisson integration
Redisson version _SpringBoot Redisson(Cluster version)
2. o.redisson.client.handler.CommandsQueue : Exception occured. Channel:
Initially using an older version of Redisson, I found that the console threw this exception shortly after the project started
Reference:
www.cnblogs.com/junge8618/p…
www.jianshu.com/p/a89dbefb8…
Switch to redisson to resolve this exception.
Other reference blogs:
Integrate SpringBoot Redisson
REDIS distributed lock REDISSON Extension This blog post integrates REDISSON locks with AOP and is worth learning about.
Redisson: How does redisson solve deadlocks
Redisson (1) Distributed Locks — How to solve deadlock problems
Use Redisson for distributed locking
SpringBoot integrates Redis with Redisson to implement a simple distributed lock