1 Distributed lock
Java locks can ensure that multiple threads in a JVM process use resources alternately. Distributed locks ensure that multiple JVM processes use resources alternately in an orderly manner, ensuring data integrity and consistency.
Distributed lock requirements
Mutual Exclusion. A resource can only be accessed by one thread at a certain moment. Avoid deadlocks. Avoid a certain thread exception not releasing resources, causing deadlocks. Reenterable. Highly available. high performance. Non-blocking, failure to return directly without obtaining the lock.
2 Implementation
1 lua script
To achieve the atomicity of the redis operation, a lua script is used. To facilitate script modification, write the script separately in the file.
-- Lock script if ('setnx', KEYS[1], ARGV[1]) == 1 then ('pexpire', KEYS[1], ARGV[2]); return true; else return false; end -- Unlock script if ('get', KEYS[1]) == ARGV[1] then ('del', KEYS[1]); return true; else return false; end -- Update lock script if ('get', KEYS[1]) == ARGV[1] then ('pexpire', KEYS[1], ARGV[2]); -- pexpireandexpireThe difference is:pexpireMillisecond level,expireSeconds return true; else return false; end
Install the script in the Springboot container managed bean.
@Configuration public class RedisConfig { @Bean("lock") public RedisScript<Boolean> lockRedisScript() { DefaultRedisScript redisScript = new DefaultRedisScript<>(); (); (new ResourceScriptSource(new ClassPathResource("/ratelimit/"))); return redisScript; } @Bean("unlock") public RedisScript<Boolean> unlockRedisScript() { DefaultRedisScript redisScript = new DefaultRedisScript<>(); (); (new ResourceScriptSource(new ClassPathResource("/ratelimit/"))); return redisScript; } @Bean("refresh") public RedisScript<Boolean> refreshRedisScript() { DefaultRedisScript redisScript = new DefaultRedisScript<>(); (); (new ResourceScriptSource(new ClassPathResource("/ratelimit/"))); return redisScript; } }
Redis distributed lock business class
@Service public class LockService { private static final long LOCK_EXPIRE = 30_000; private static final Logger LOGGER = (); @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired @Qualifier("lock") private RedisScript<Boolean> lockScript; @Autowired @Qualifier("unlock") private RedisScript<Boolean> unlockScript; @Autowired @Qualifier("refresh") private RedisScript<Boolean> refreshScript; public boolean lock(String key, String value) { boolean res = (lockScript, (key), value, LOCK_EXPIRE); if (res == false) { return false; } refresh(key, value); ("lock, key: {}, value: {}, res: {}", key, value, res); return res; } public boolean unlock(String key, String value) { Boolean res = (unlockScript, (key), value); ("unlock, key: {}, value: {}, res: {}", key, value, res); return res != null && (res); } private void refresh(String key, String value) { Thread t = new Thread(() -> { while (true) { (refreshScript, (key), value, LOCK_EXPIRE); try { (LOCK_EXPIRE / 2); } catch (InterruptedException e) { (); } ("refresh, current time: {}, key: {}, value: {}", (), key, value); } }); (true); // Daemon thread (); } }
Test class
@SpringBootTest(classes = ) public class LockServiceTest { @Autowired private LockService service; private int count = 0; @Test public void test() throws Exception { List<CompletableFuture<Void>> taskList = new ArrayList<>(); for (int threadIndex = 0; threadIndex < 10; threadIndex++) { CompletableFuture<Void> task = (() -> addCount()); (task); } ((new CompletableFuture[0])).join(); } public void addCount() { String id = ().toString().replace("-", ""); boolean tryLock = ("account", id); while (!tryLock) { tryLock = ("account", id); } for (int i = 0; i < 10_000; i++) { count++; } try { (100_000); } catch (Exception e) { (e); } for (int i = 0; i < 3; i++) { boolean releaseLock = ("account", id); if (releaseLock) { break; } } } }
3 Existing problems
This distributed lock implements mutual exclusion, redis key maps resources, and if there is a key, the resource is being held by a certain thread. If no key exists, the resource is idle.
To avoid deadlock, we rely on setting the expiration time of the reds key, and at the same time, turning on the daemon thread to dynamically extend the expiration time of the redis key until the thread task is completed.
high performance. Redis is an in-memory database with high performance. At the same time, the lua script allows redis to update the lock state atomically, avoiding multiple network IOs of the spirngboot and redis.
Non-blocking.lock()
The method returns false immediately after not obtaining the lock, and will not block the current thread.
No reentrability and high availability are achieved. High availability requires redis cluster support.
This is the end of this article about springboot+redis+lua implementing distributed locks. For more related contents of springboot redis lua distributed locks, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!