In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >
Share
Shulou(Shulou.com)05/31 Report--
This article is about how to use Redis to implement a secure and reliable distributed lock. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.
In a concurrent scenario, multiple processes or threads share the read and write of resources, so it is necessary to ensure that access to resources is mutually exclusive. In the stand-alone system, we can use the API and synchronized keywords in the Java concurrent package to solve the problem, but in the distributed system, these methods are no longer applicable, we need to implement the distributed lock ourselves.
The common implementation schemes of distributed lock are: based on database, based on Redis, based on Zookeeper and so on. As part of the Redis project, this article will talk about the implementation of distributed locks based on Redis.
Analysis and implementation problem analysis
Distributed locks and JVM built-in locks have a common purpose: to enable applications to access or operate shared resources in the expected order, to prevent multiple threads from operating on the same resource at the same time, resulting in disordered and uncontrollable system operation. It is often used in scenarios such as inventory deduction and coupon deduction.
In theory, in order to ensure the security and effectiveness of locks, distributed locks need to meet at least the following conditions:
Mutex: only one thread can acquire a lock at a time
No deadlock: after the thread acquires the lock, it must be able to release, even if the application goes down after the thread acquires the lock, it can be released within a limited time.
Locking and unlocking must be the same thread
In terms of implementation, distributed locking is generally divided into three steps:
A-obtain the right to operate the resource
B-perform actions on resources
C-the right of operation to release resources
Whether it is the lock built in Java or the distributed lock, no matter which distributed implementation scheme is used, it is carried out around two steps an and c. Redis is naturally friendly to implementing distributed locks for the following reasons:
In the command processing phase, Redis uses single-thread processing, and only one thread can handle the same key at the same time, so there is no multi-thread race problem.
The SET key value NX PX milliseconds command adds a key with expiration time in the absence of key to provide support for secure locking.
Lua scripts and DEL commands provide reliable support for secure unlocking.
Code implementation
Maven dependence
Org.springframework.boot spring-boot-starter-data-redis ${your-spring-boot-version}
Configuration file
Add the following to application.properties, stand-alone Redis instance.
Spring.redis.database=0spring.redis.host=localhostspring.redis.port=6379
RedisConfig
@ Configurationpublic class RedisConfig {/ / define a RedisTemplate @ Bean @ SuppressWarnings ("all") public RedisTemplate redisTemplate (RedisConnectionFactory factory) throws UnknownHostException {/ / We usually directly use RedisTemplate template = new RedisTemplate (); template.setConnectionFactory (factory); / / Json serialization configuration Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer (Object.class) for the convenience of our own development ObjectMapper om = new ObjectMapper (); om.setVisibility (PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping (ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper (om); / / serialization of String StringRedisSerializer stringRedisSerializer = new StringRedisSerializer (); / / key adopts the serialization method of String template.setKeySerializer (stringRedisSerializer) / / key of hash also adopts the serialization method of String template.setHashKeySerializer (stringRedisSerializer); / / value serialization adopts jackson template.setValueSerializer (jackson2JsonRedisSerializer); / / value serialization of hash adopts jackson template.setHashValueSerializer (jackson2JsonRedisSerializer); template.afterPropertiesSet (); return template;}}
RedisLock
@ Servicepublic class RedisLock {@ Resource private RedisTemplate redisTemplate / * Lock, wait up to maxWait millisecond * * @ param lockKey Lock key * @ param lockValue Lock value * @ param timeout Lock time (Ms) * @ param maxWait Lock wait time (Ms) * @ return true- successful False- failed * / public boolean tryAcquire (String lockKey, String lockValue, int timeout, long maxWait) {long start = System.currentTimeMillis () While (true) {/ / attempt to lock Boolean ret = redisTemplate.opsForValue (). SetIfAbsent (lockKey, lockValue, timeout, TimeUnit.MILLISECONDS); if (! ObjectUtils.isEmpty (ret) & & ret) {return true;} / / calculate the time long now = System.currentTimeMillis () If (now-start > maxWait) {return false;} try {Thread.sleep;} catch (Exception ex) {return false } / * * release lock * * @ param lockKey lock key * @ param lockValue lock value * @ return true- successful False- failed * / public boolean releaseLock (String lockKey, String lockValue) {/ / lua script String script = "if redis.call ('get',KEYS [1]) = = ARGV [1] then return redis.call (' del',KEYS [1]) else return 0 end" DefaultRedisScript redisScript = new DefaultRedisScript (script, Long.class); Long result = redisTemplate.opsForValue (). GetOperations (). Execute (redisScript, Collections.singletonList (lockKey), lockValue); return result! = null & & result > 0L;}}
Test case
@ SpringBootTestclass RedisDistLockDemoApplicationTests {@ Resource private RedisLock redisLock; @ Test public void testLock () {redisLock.tryAcquire ("abcd", "abcd", 5 * 60 * 1000, 5 * 1000); redisLock.releaseLock ("abcd", "abcd");}} security risks
Perhaps many students (including me) use the above implementation in their daily work, which seems to be safe:
Use set commands NX and PX to add locks to ensure mutual exclusion and avoid deadlocks.
Use lua scripts to unlock to prevent other threads from unlocking
Locking and unlocking commands are atomic operations.
In fact, there is a prerequisite for the above implementation: stand-alone version of Redis, enable AOF persistence mode and set appendfsync=always.
But there may be problems in Sentinel mode and cluster mode. Why?
Sentinel mode and cluster mode are based on master-slave architecture. Master-slave data synchronization is achieved through command propagation, while command propagation is asynchronous.
Therefore, it is possible that the master node data is successfully written, and the master node will be down without notifying the slave node.
When the slave node is promoted to the new master node through failover, other threads have a chance to re-lock successfully, resulting in not satisfying the mutual exclusion condition of the distributed lock.
Official RedLock
In cluster mode, if all nodes in the cluster run stably and there is no failover, the security is guaranteed. However, no system can guarantee 100% stability, and fault tolerance must be taken into account in Redis-based distributed locks.
Because master-slave synchronization is based on the principle of asynchronous replication, Sentinel mode and cluster mode are inherently unable to meet this condition. To this end, the author of Redis specially proposed a solution-RedLock (Redis Distribute Lock).
Design ideas
According to the description of the official document, the design idea of RedLock is introduced.
First of all, the environment requires N (N > = 3) independently deployed Redis instances, and there is no need for master-slave replication, failover and other technologies.
In order to acquire the lock, the client follows the following process:
Get the current time (milliseconds) as the start time start
Using the same key and random value, request for lock is initiated to all N nodes sequentially. When a lock is set on each instance, the client uses an expiration time (less than the automatic release time of the lock). For example, the automatic release time of the lock is 10 seconds, and the timeout should be 5-50 milliseconds. This is to prevent the client from wasting too much time on an instance that is already down: if the Redis instance goes down, the client processes the next instance as soon as possible.
The client calculates the time consumed by locking cost (cost=start-now). Only when the client successfully locks more than half of the instances, and the whole time is less than the whole effective time (ttl), can the current client be considered to be locked successfully.
If the client locks successfully, then the real validity time of the whole lock should be: validTime=ttl-cost.
If the client fails to lock (either because the number of successful instances of the lock is less than half, or it takes more than ttl), the client should try to unlock all instances (even if the client just thought the locking failed).
The design idea of RedLock continues the voting scheme of multiple scenarios within Redis, which solves the race problem by adding locks respectively through multiple examples. Although locking takes time, it eliminates the security problems under the master-slave mechanism.
Code implementation
It is officially recommended that Java be implemented as Redisson, which is reentrant and implemented according to RedLock, and supports independent instance mode, cluster mode, master-slave mode, sentry mode, etc. API is relatively simple and easy to use. The example is as follows (directly through the test case):
@ Test public void testRedLock () throws InterruptedException {Config config = new Config (); config.useSingleServer () .setAddress ("redis://127.0.0.1:6379"); final RedissonClient client = Redisson.create (config); / / acquire lock instance final RLock lock = client.getLock ("test-lock"); / / lock lock.lock (60 * 1000, TimeUnit.MILLISECONDS) Try {/ / pretend to do something Thread.sleep (50 * 1000);} catch (Exception ex) {ex.printStackTrace ();} finally {/ / unlock lock.unlock ();}}
Redisson is encapsulated so well that we can use it like Java's built-in locks, and the code is as concise as it can be. On the analysis of Redisson source code, there are many articles on the Internet that you can look for.
Full-text summary
Distributed lock is a common way to solve the concurrency problem in our research and development process, and Redis is just an implementation.
The key is to understand the principle behind locking and unlocking, as well as the core problems that need to be solved to realize distributed locking, and to consider what features of the middleware we use can support it. After understanding this, it will not be a problem to realize it.
Thank you for reading! This is the end of the article on "how to use Redis to achieve a secure and reliable distributed lock". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can 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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.