이번 글에서는 Zookeeper의 클라이언트 스레딩 모델에 대해 자세히 알아보고, Java API를 활용하여 Zookeeper와 연결하는 방법까지 단계별로 소개하겠습니다. 이 글을 읽는 독자는 Gradle 기반 프로젝트를 기준으로 따라 할 수 있습니다.
Zookeeper 클라이언트 스레딩 모델
Zookeeper 클라이언트를 생성하면 내부적으로 2개의 추가 스레드가 자동 생성됩니다.
1. IO 스레드
- 네트워크 통신 처리 담당
- Zookeeper 서버와의 세션 유지, 요청 및 응답 처리, 세션 타임아웃 관리 등 수행
- 사용자 코드와 직접 상호작용하지 않음
2. 이벤트 스레드
- 서버와의 연결 및 해제 등 상태 변화 이벤트 처리
- 사용자가 등록한 Watcher 트리거 관리
- 이벤트는 발생 순서대로 하나씩 처리됨
즉, Zookeeper는 이벤트 기반 API로 동작하며, 이벤트 처리 흐름은 단일 스레드로 직렬 실행됩니다.
Maven 프로젝트 설정
pom.xml 파일은 다음과 같이 작성합니다:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>distributed.systems</groupId>
<artifactId>leader.election</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<mainClass>LeaderElection</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.12</version>
</dependency>
</dependencies>
</project>
Gradle 프로젝트 설정
build.gradle 파일은 다음과 같이 작성합니다:
plugins {
id 'java'
id 'application'
}
group = 'distributed.systems'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.apache.zookeeper:zookeeper:3.4.12'
}
application {
mainClass = 'LeaderElection'
}
tasks.withType(JavaCompile) {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
tasks.register('fatJar', Jar) {
manifest {
attributes 'Main-Class': application.mainClass
}
archiveClassifier.set('all')
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}
LeaderElection.java 코드 작성
import org.apache.zookeeper.*;
import java.io.IOException;
public class LeaderElection implements Watcher {
private static final String ZOOKEEPER_ADDRESS = "localhost:2181";
private static final int SESSION_TIMEOUT = 3000;
private ZooKeeper zooKeeper;
public static void main(String[] arg) throws IOException, InterruptedException, KeeperException {
LeaderElection leaderElection = new LeaderElection();
leaderElection.connectToZookeeper();
leaderElection.run();
leaderElection.close();
System.out.println("Disconnected from Zookeeper, exiting application");
}
public void connectToZookeeper() throws IOException {
this.zooKeeper = new ZooKeeper(ZOOKEEPER_ADDRESS, SESSION_TIMEOUT, this);
}
private void run() throws InterruptedException {
synchronized (zooKeeper) {
zooKeeper.wait();
}
}
private void close() throws InterruptedException {
this.zooKeeper.close();
}
@Override
public void process(WatchedEvent event) {
switch (event.getType()) {
case None:
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("Successfully connected to Zookeeper");
} else {
synchronized (zooKeeper) {
System.out.println("Disconnected from Zookeeper event");
zooKeeper.notifyAll();
}
}
}
}
}
log4j 설정 파일 추가
리소스 디렉토리 안에 log4j.properties 파일을 만들어 아래 내용을 추가합니다:
log4j.rootLogger=DEBUG, zookeeper
log4j.appender.zookeeper=org.apache.log4j.ConsoleAppender
log4j.appender.zookeeper.Target=System.out
log4j.appender.zookeeper.layout=org.apache.log4j.PatternLayout
log4j.appender.zookeeper.layout.ConversionPattern=%d{HH:mm:ss} %-5p [%c] - %m%n
필요 시 DEBUG를 WARN으로 낮추어 불필요한 로그 출력을 줄일 수 있습니다.
실행과 디버깅
- 프로그램을 실행하면 이벤트 스레드에서 연결 메시지를 출력합니다.
- Zookeeper 서버를 종료하면 연결 해제 이벤트가 발생합니다.
- 이벤트 핸들러에서 메인 스레드를 깨우고 종료 처리를 합니다.
지속적으로 핑을 보내서 확인하는 모습이다.
주키퍼 서버를 정지했더니, 이벤트 핸들러가 실행되어 문구가 출력된것을 볼 수 있다.
마무리
이번 글에서는 Zookeeper 클라이언트의 스레딩 모델 구조를 이해하고, Java API를 통해 Zookeeper와 연결하는 간단한 애플리케이션을 구현해보았습니다.
이 흐름은 이후 구현할 리더 선출 알고리즘의 기초가 됩니다.
다음 글에서는 이 구조 위에 리더 선출 로직을 덧붙여보겠습니다.
계속해서 리더 선출 알고리즘 구현 편으로 이어집니다.
반응형
'Server-side 개발 & 트러블 슈팅 > 🦍 ZooKeeper (주키퍼)' 카테고리의 다른 글
[ZooKeeper] 주키퍼 기반 리더 선출 알고리즘 구현 (2) | 2025.04.12 |
---|---|
[ZooKeeper] 주키퍼 설치 및 설정 가이드 (Mac OS, Windows, Linux) (0) | 2025.04.12 |
[ZooKeeper] 코디네이터(Coordinator)개념과 주키퍼 소개 (0) | 2025.04.12 |
댓글