An overview of the

When it comes to distributed locks, the following two types come to mind:

  • Based on theRedissonComponent, using the Redlock algorithm
  • Based on theApache Curator, using the temporary sequential node model of Zookeeper

Today we will talk about the third one, implemented using Spring Integration, which is also the one I personally recommend.

Spring Integration enables lightweight messaging in Spring-based applications and enables Integration with external systems through declared adapters. The primary goal of Spring Integration is to provide a simple model to build enterprise Integration solutions while maintaining the separation of concerns that is critical to generating maintainable, testable code. Spring Integration is the underlying Spring Cloud Stream.

Official address: github.com/spring-proj…

Global locking provided by Spring Integration currently provides an implementation for the following storage:

  • Gemfire
  • JDBC
  • Redis
  • Zookeeper

They use the same API abstraction, which means your coding experience is the same regardless of which storage you use. Imagine that you currently have a distributed lock based on the ZooKeeper implementation, and one day you want to switch to the Redis implementation, all we need to do is change the dependencies and configuration, without changing the code. Here’s what you need to look for when implementing distributed locking using Spring Integration:

The method name describe
lock() Acquires the lock.Locks and blocks if it is already locked by another thread or the current thread cannot acquire the lock
lockInterruptibly() Acquires the lock unless the current thread is interrupted.Lock unless the current thread is interrupted.
tryLock() Acquires the lock only if it is free at the time of invocation.If the current thread cannot lock, return false, the lock failed. Return true if the lock succeeded
tryLock(long time, TimeUnit unit) Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted.If the current thread cannot be locked, return false, the lock failed. Return true if the lock succeeded
unlock() Releases the lock.unlock

In actual combat

Without further ado, let’s see how to quickly implement distributed locks using Spring Integration based on Redis and ZooKeeper, as well as Gemfire and Jdbc implementations.

Based on Redis implementation

  • Importing related components
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-integration</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.integration</groupId>
	<artifactId>spring-integration-redis</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code
  • Add the redis configuration in application.yml
spring:
	redis:
		host: 172.31. 0149.
		port: 7111
Copy the code
  • Create a configuration class and injectRedisLockRegistry
@Configuration
public class RedisLockConfiguration {

    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory){
        return new RedisLockRegistry(redisConnectionFactory, "redis-lock"); }}Copy the code
  • Write test code
@RestController
@RequestMapping("lock")
@Log4j2
public class DistributedLockController {
    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @GetMapping("/redis")
    public void test1(a) {
        Lock lock = redisLockRegistry.obtain("redis");
        try{
            If the current thread cannot lock, return false, the lock failed. Return true if the lock succeeded
            if(lock.tryLock(3, TimeUnit.SECONDS)){
                log.info("lock is ready");
                TimeUnit.SECONDS.sleep(5); }}catch (InterruptedException e) {
            log.error("obtain lock error",e);
        } finally{ lock.unlock(); }}}Copy the code
  • The test starts multiple instances, accessed separately/lock/redisEndpoint, a normal order business logic, another instance access error

    The second instance did not get the lock, proving the existence of a distributed lock.

Note That Redis4 is required for Springboot integration. Otherwise, the following exceptions are generated. The UNLINK command is used to release the unlock() lock, which can only be supported by Redis4.

2020-05-14 11:30:24,781 WARN  RedisLockRegistry:339 - The UNLINK commandhas failed (not supported on the Redis server?) ; falling back to the regular DELETEcommand
org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'UNLINK'
Copy the code

Implemented based on Zookeeper

  • The introduction of the component
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-integration</artifactId>
</dependency>

 <dependency>
	<groupId>org.springframework.integration</groupId>
	<artifactId>spring-integration-zookeeper</artifactId>
</dependency>
Copy the code
  • Add the ZooKeeper configuration in application.yml
zookeeper:  
    host: 172.31. 043.: 2181
Copy the code
  • Create a configuration class and injectZookeeperLockRegistry
@Configuration
public class ZookeeperLockConfiguration {
    @Value("${zookeeper.host}")
    private String zkUrl;


    @Bean
    public CuratorFrameworkFactoryBean curatorFrameworkFactoryBean(a){
        return new CuratorFrameworkFactoryBean(zkUrl);
    }

    @Bean
    public ZookeeperLockRegistry zookeeperLockRegistry(CuratorFramework curatorFramework){
        return new ZookeeperLockRegistry(curatorFramework,"/zookeeper-lock"); }}Copy the code
  • Write test code
@RestController
@RequestMapping("lock")
@Log4j2
public class DistributedLockController {

    @Autowired
    private ZookeeperLockRegistry zookeeperLockRegistry;

    @GetMapping("/zookeeper")
    public void test2(a) {
        Lock lock = zookeeperLockRegistry.obtain("zookeeper");
        try{
            If the current thread cannot lock, return false, the lock failed. Return true if the lock succeeded
            if(lock.tryLock(3, TimeUnit.SECONDS)){
                log.info("lock is ready");
                TimeUnit.SECONDS.sleep(5); }}catch (InterruptedException e) {
            log.error("obtain lock error",e);
        } finally{ lock.unlock(); }}}Copy the code
  • The test starts multiple instances, accessed separately/lock/zookeeperEndpoint, a normal order business logic, another instance access error

    The second instance did not get the lock, proving the existence of a distributed lock.


Here for everyone to prepare a small gift, pay attention to the public number, enter the following code, you can get baidu network disk address, no routine to receive!

004: “Internet Architecture Teaching Video” 006: “SpringBoot Implementation of Ordering System” 007: “SpringSecurity combat video” 008: “Hadoop combat teaching video” 009: “Tencent 2019Techo Developer Conference PPT” 010: wechat communication group