Generate Distributed IDs with Zookeeper and Spring Boot

This guide shows how to use Zookeeper 3.6.2 with Spring Boot 2.2.10 to create sequential nodes, implement a distributed ID service in Java, and test concurrent ID generation across 100 threads.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Generate Distributed IDs with Zookeeper and Spring Boot

Zookeeper version: 3.6.2

Since Zookeeper 3.6, watches can be recursive, unlike earlier versions where a watch was removed after a change.

Create a sequential node via command line:

create /distributed/key/ids   # create a persistent node
Created /distributed/key/ids

create -s /distributed/key/ids/  # create sequential node under ids
Created /distributed/key/ids/0000000000

# Repeating the command continues the sequence even after deletion.

Next, implement with Java.

Environment: Spring Boot 2.2.10.RELEASE, JDK 1.8, Zookeeper 3.6.2

pom.xml

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.6.2</version>
  </dependency>
</dependencies>

application.yml configuration

server:
  port: 9881
---
zk:
  connectString: localhost:2181,localhost:2182,localhost:2183
  timeout: 3000

Zookeeper connection configuration

@Configuration
public class ZKConfig {
    private static Logger logger = LoggerFactory.getLogger(ZKConfig.class);

    @Value("${zk.connectString}")
    private String connectString;

    @Value("${zk.timeout}")
    private Integer timeout;

    @Bean
    public ZooKeeper zookeeper() {
        ZooKeeper zookeeper = null;
        try {
            CountDownLatch cdl = new CountDownLatch(1);
            logger.info("zk准备连接: {}", connectString);
            zookeeper = new ZooKeeper(connectString, timeout, event -> {
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    logger.info("zk服务器连接成功");
                    cdl.countDown();
                }
            });
            cdl.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return zookeeper;
    }
}

Implementation

public interface DistributedIdModel {
    Long getId(String key);
}

@Service
public class DistributedIdZK implements DistributedIdModel {
    private static final String PATH = "/distributed/key/ids/";
    @Resource
    private ZooKeeper zk;

    @Override
    public Long getId(String key) {
        try {
            String res = zk.create(PATH, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
            zk.delete(res, 0);
            if (res != null && res.length() > PATH.length()) {
                return Long.valueOf(res.substring(PATH.length()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

zk.create returns the full path; the node is then deleted to avoid accumulation.

Test

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringDistributedIdApplicationTests {
    @Resource
    private DistributedIdModel distId;
    private final int COUNT = 100;
    private CountDownLatch cdl = new CountDownLatch(COUNT);
    private CountDownLatch state = new CountDownLatch(COUNT);
    private Thread[] threads = new Thread[COUNT];

    @Before
    public void before() {
        for (int i = 0; i < COUNT; i++) {
            threads[i] = new Thread(() -> {
                cdl.countDown();
                System.out.println(Thread.currentThread().getName() + ", 到达");
                try {
                    cdl.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Long id = distId.getId(null);
                System.out.println(Thread.currentThread().getName() + ", 获取到ID = " + id);
                state.countDown();
            }, "线程-" + i);
        }
    }

    @Test
    public void testGetRedisId() {
        for (int i = 0; i < COUNT; i++) {
            threads[i].start();
        }
        try {
            state.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

This simulates 100 concurrent threads obtaining IDs.

Done!

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyZooKeeperSpring Bootdistributed-idsequential node
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.