Distributed Locking with Redis Cluster Using Redisson

To implement distributed locking in a Redis cluster environment, we leverage Redisson, a Java-based client that simplifies working with Redis in distributed systems.

Environment Setup

Component Version
Spring Boot 2.0.3.RELEASE
Spring Cloud Finchley.RELEASE
Redis 4.0.11
JDK 1.8.x

Maven Dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
</parent>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.5.4</version>
    </dependency>
</dependencies>

Redisson Overview

Redisson is a Redis-based In-Memory Data Grid for Java built on top of Netty. It provides distributed implementations of common Java utilities such as locks, atomic counters, and executors—enabling coordination across multiple JVMs in a cluster.

It supports use cases like distributed caching, session management, task scheduling, and more. Configuration is required before usage, similar to setting up a Jedis connection pool.

Configuration Approach

Three configuration styles are available:

  1. Pure Java: All settings defined programmatically (used here).
  2. XML-based Spring: Legacy and verbose; not recommended.
  3. External config file: Parameters loaded from YAML/properties files.

Redisson Client Initialization

import org.redisson.Redisson;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonManager {
    private static Config config = new Config();
    private static RedissonClient client = null;
    private static final String ATOMIC_COUNTER_KEY = "genId_";

    public static void initialize() {
        try {
            config.useClusterServers()
                .setScanInterval(200_000)
                .setMasterConnectionPoolSize(10_000)
                .setSlaveConnectionPoolSize(10_000)
                .setIdleConnectionTimeout(10_000)
                .setConnectTimeout(30_000)
                .setTimeout(3_000)
                .setRetryInterval(3_000)
                .addNodeAddress(
                    "redis://127.0.0.1:7000",
                    "redis://127.0.0.1:7001",
                    "redis://127.0.0.1:7002",
                    "redis://127.0.0.1:7003",
                    "redis://127.0.0.1:7004",
                    "redis://127.0.0.1:7005"
                );

            client = Redisson.create(config);
            RAtomicLong counter = client.getAtomicLong(ATOMIC_COUNTER_KEY);
            counter.set(0); // reset counter
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static RedissonClient getClient() {
        if (client == null) {
            initialize();
        }
        return client;
    }

    public static long getNextId() {
        return getClient().getAtomicLong(ATOMIC_COUNTER_KEY).incrementAndGet();
    }
}

Distributed Lock Utility

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
public class DistributedLock {
    private static final Logger logger = LoggerFactory.getLogger(DistributedLock.class);
    private static final RedissonClient redisson = RedissonManager.getClient();

    public void acquire(String lockKey) {
        RLock lock = redisson.getLock(lockKey);
        lock.lock(2, TimeUnit.SECONDS); // auto-unlock after 2s
        System.out.println("Acquired lock: " + Thread.currentThread().getName());
    }

    public void release(String lockKey) {
        RLock lock = redisson.getLock(lockKey);
        lock.unlock();
        System.out.println("Released lock: " + Thread.currentThread().getName());
    }
}

Integration Test

@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan({"com.annotaion", "cn.springcloud", "com.config", "com.redislock"})
public class Application implements ApplicationRunner {

    @Autowired
    private DistributedLock lockUtil;

    private static final ExecutorService executor = Executors.newFixedThreadPool(5);
    private static final CyclicBarrier barrier = new CyclicBarrier(5);

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(ApplicationArguments args) {
        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " waiting at barrier");
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + " proceeding");

                    String resource = "test123";
                    lockUtil.acquire(resource);
                    Thread.sleep(1000); // simulate work
                    System.out.println(Thread.currentThread().getName() + " processing...");
                    lockUtil.release(resource);
                    System.out.println(Thread.currentThread().getName() + " done");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();
        long id = RedissonManager.getNextId();
        System.out.println("Final atomic ID from Redis: " + id);
    }
}

Key Observations

  • In a single Redis instance, failure causes total lock unavailability—requiring fallback strategies.
  • Lock expiration must exceed task duration to avoid premature release and race conditions.
  • Redis Cluster offers higher availability: partial node failures don’t break locking.
  • If a failed node restarts quickly and uses persistence, the risk of duplicate lock ownership decreases.
  • Redisson abstracts differences between standalone and clustered Redis locking mechanisms, ensuring consistent behavior.

Tags: Redis Redisson DistributedLock RedisCluster SpringBoot

Posted on Mon, 21 Sep 2026 16:24:29 +0000 by NTFS