Skip to content

Commit 0a22a8e

Browse files
committed
Leader election implementation using ZooKeeper
Leader election implementation using Apache ZooKeeper
1 parent 6ed1208 commit 0a22a8e

File tree

9 files changed

+350
-0
lines changed

9 files changed

+350
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@
1010

1111
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
1212
hs_err_pid*
13+
*.lock
14+
.metadata
15+
RemoteSystemsTempFiles
16+
.settings
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<classpath>
3+
<classpathentry kind="src" output="target/classes" path="src/main/java">
4+
<attributes>
5+
<attribute name="optional" value="true"/>
6+
<attribute name="maven.pomderived" value="true"/>
7+
</attributes>
8+
</classpathentry>
9+
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
10+
<attributes>
11+
<attribute name="maven.pomderived" value="true"/>
12+
</attributes>
13+
</classpathentry>
14+
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
15+
<attributes>
16+
<attribute name="optional" value="true"/>
17+
<attribute name="maven.pomderived" value="true"/>
18+
</attributes>
19+
</classpathentry>
20+
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
21+
<attributes>
22+
<attribute name="maven.pomderived" value="true"/>
23+
</attributes>
24+
</classpathentry>
25+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
26+
<attributes>
27+
<attribute name="maven.pomderived" value="true"/>
28+
</attributes>
29+
</classpathentry>
30+
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
31+
<attributes>
32+
<attribute name="maven.pomderived" value="true"/>
33+
</attributes>
34+
</classpathentry>
35+
<classpathentry kind="output" path="target/classes"/>
36+
</classpath>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectDescription>
3+
<name>leader-election</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
<buildCommand>
14+
<name>org.eclipse.m2e.core.maven2Builder</name>
15+
<arguments>
16+
</arguments>
17+
</buildCommand>
18+
</buildSpec>
19+
<natures>
20+
<nature>org.eclipse.jdt.core.javanature</nature>
21+
<nature>org.eclipse.m2e.core.maven2Nature</nature>
22+
</natures>
23+
</projectDescription>
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>com.sts.allprogtutorials.apachezookeeper</groupId>
5+
<artifactId>leader-election</artifactId>
6+
<version>0.0.1-SNAPSHOT</version>
7+
<name>Leader Election</name>
8+
9+
<dependencies>
10+
<dependency>
11+
<groupId>org.apache.zookeeper</groupId>
12+
<artifactId>zookeeper</artifactId>
13+
<version>3.4.6</version>
14+
</dependency>
15+
16+
</dependencies>
17+
18+
<build>
19+
<plugins>
20+
<plugin>
21+
<groupId>org.apache.maven.plugins</groupId>
22+
<artifactId>maven-compiler-plugin</artifactId>
23+
<version>3.3</version>
24+
<configuration>
25+
<source>1.7</source>
26+
<target>1.7</target>
27+
</configuration>
28+
</plugin>
29+
<plugin>
30+
<groupId>org.apache.maven.plugins</groupId>
31+
<artifactId>maven-shade-plugin</artifactId>
32+
<version>2.3</version>
33+
<executions>
34+
<execution>
35+
<phase>package</phase>
36+
<goals>
37+
<goal>shade</goal>
38+
</goals>
39+
</execution>
40+
</executions>
41+
</plugin>
42+
</plugins>
43+
</build>
44+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
*
3+
*/
4+
package com.sts.allprogtutorials.zk.leaderelection.main;
5+
6+
import java.io.IOException;
7+
import java.util.concurrent.ExecutionException;
8+
import java.util.concurrent.ExecutorService;
9+
import java.util.concurrent.Executors;
10+
import java.util.concurrent.Future;
11+
12+
import org.apache.log4j.Logger;
13+
14+
import com.sts.allprogtutorials.zk.leaderelection.nodes.ProcessNode;
15+
16+
/**
17+
* @author Sain Technology Solutions
18+
*
19+
*/
20+
public class LeaderElectionLauncher {
21+
22+
private static final Logger LOG = Logger.getLogger(LeaderElectionLauncher.class);
23+
24+
public static void main(String[] args) throws IOException {
25+
26+
final String zkURL = "10.127.128.56:2181";
27+
28+
final ExecutorService service = Executors.newSingleThreadExecutor();
29+
30+
final Future<?> status = service.submit(new ProcessNode(4, zkURL));
31+
32+
try {
33+
status.get();
34+
} catch (InterruptedException | ExecutionException e) {
35+
LOG.fatal(e.getMessage(), e);
36+
service.shutdown();
37+
}
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
*
3+
*/
4+
package com.sts.allprogtutorials.zk.leaderelection.nodes;
5+
6+
import java.io.IOException;
7+
import java.util.Collections;
8+
import java.util.List;
9+
10+
import org.apache.log4j.Logger;
11+
import org.apache.zookeeper.WatchedEvent;
12+
import org.apache.zookeeper.Watcher;
13+
import org.apache.zookeeper.Watcher.Event.EventType;
14+
15+
import com.sts.allprogtutorials.zk.utils.ZooKeeperService;
16+
17+
/**
18+
* @author Sain Technology Solutions
19+
*
20+
*/
21+
public class ProcessNode implements Runnable{
22+
23+
private static final Logger LOG = Logger.getLogger(ProcessNode.class);
24+
25+
private static final String LEADER_ELECTION_ROOT_NODE = "/election";
26+
private static final String PROCESS_NODE_PREFIX = "/p_";
27+
28+
private final int id;
29+
private final ZooKeeperService zooKeeperService;
30+
31+
private String processNodePath;
32+
private String watchedNodePath;
33+
34+
35+
public ProcessNode(final int id, final String zkURL) throws IOException {
36+
this.id = id;
37+
zooKeeperService = new ZooKeeperService(zkURL, new ProcessNodeWatcher());
38+
}
39+
40+
private void attemptForLeaderPosition() {
41+
42+
final List<String> childNodePaths = zooKeeperService.getChildren(LEADER_ELECTION_ROOT_NODE, false);
43+
44+
Collections.sort(childNodePaths);
45+
46+
int index = childNodePaths.indexOf(processNodePath.substring(processNodePath.lastIndexOf('/') + 1));
47+
if(index == 0) {
48+
if(LOG.isInfoEnabled()) {
49+
LOG.info("[Process: " + id + "] I am the new leader!");
50+
}
51+
} else {
52+
final String watchedNodeShortPath = childNodePaths.get(index - 1);
53+
54+
watchedNodePath = LEADER_ELECTION_ROOT_NODE + "/" + watchedNodeShortPath;
55+
56+
if(LOG.isInfoEnabled()) {
57+
LOG.info("[Process: " + id + "] - Setting watch on node with path: " + watchedNodePath);
58+
}
59+
zooKeeperService.watchNode(watchedNodePath, true);
60+
}
61+
}
62+
63+
@Override
64+
public void run() {
65+
66+
System.out.println("Process with id: " + id + " has started!");
67+
68+
final String rootNodePath = zooKeeperService.createNode(LEADER_ELECTION_ROOT_NODE, false, false);
69+
if(rootNodePath == null) {
70+
throw new IllegalStateException("Unable to create/access leader election root node with path: " + LEADER_ELECTION_ROOT_NODE);
71+
}
72+
73+
processNodePath = zooKeeperService.createNode(rootNodePath + PROCESS_NODE_PREFIX, false, true);
74+
if(processNodePath == null) {
75+
throw new IllegalStateException("Unable to create/access process node with path: " + LEADER_ELECTION_ROOT_NODE);
76+
}
77+
78+
if(LOG.isDebugEnabled()) {
79+
LOG.debug("[Process: " + id + "] Process node created with path: " + processNodePath);
80+
}
81+
82+
attemptForLeaderPosition();
83+
}
84+
85+
public class ProcessNodeWatcher implements Watcher{
86+
87+
@Override
88+
public void process(WatchedEvent event) {
89+
if(LOG.isDebugEnabled()) {
90+
LOG.debug("[Process: " + id + "] Event received: " + event);
91+
}
92+
93+
final EventType eventType = event.getType();
94+
if(EventType.NodeDeleted.equals(eventType)) {
95+
if(event.getPath().equalsIgnoreCase(watchedNodePath)) {
96+
attemptForLeaderPosition();
97+
}
98+
}
99+
100+
}
101+
102+
}
103+
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
*
3+
*/
4+
package com.sts.allprogtutorials.zk.utils;
5+
6+
import java.io.IOException;
7+
import java.util.List;
8+
9+
import org.apache.zookeeper.CreateMode;
10+
import org.apache.zookeeper.KeeperException;
11+
import org.apache.zookeeper.ZooDefs.Ids;
12+
import org.apache.zookeeper.ZooKeeper;
13+
import org.apache.zookeeper.data.Stat;
14+
15+
import com.sts.allprogtutorials.zk.leaderelection.nodes.ProcessNode.ProcessNodeWatcher;
16+
17+
/**
18+
* @author Sain Technology Solutions
19+
*
20+
*/
21+
public class ZooKeeperService {
22+
23+
private ZooKeeper zooKeeper;
24+
25+
public ZooKeeperService(final String url, final ProcessNodeWatcher processNodeWatcher) throws IOException {
26+
zooKeeper = new ZooKeeper(url, 3000, processNodeWatcher);
27+
}
28+
29+
public String createNode(final String node, final boolean watch, final boolean ephimeral) {
30+
String createdNodePath = null;
31+
try {
32+
33+
final Stat nodeStat = zooKeeper.exists(node, watch);
34+
35+
if(nodeStat == null) {
36+
createdNodePath = zooKeeper.create(node, new byte[0], Ids.OPEN_ACL_UNSAFE, (ephimeral ? CreateMode.EPHEMERAL_SEQUENTIAL : CreateMode.PERSISTENT));
37+
} else {
38+
createdNodePath = node;
39+
}
40+
41+
} catch (KeeperException | InterruptedException e) {
42+
throw new IllegalStateException(e);
43+
}
44+
45+
return createdNodePath;
46+
}
47+
48+
public boolean watchNode(final String node, final boolean watch) {
49+
50+
boolean watched = false;
51+
try {
52+
final Stat nodeStat = zooKeeper.exists(node, watch);
53+
54+
if(nodeStat != null) {
55+
watched = true;
56+
}
57+
58+
} catch (KeeperException | InterruptedException e) {
59+
throw new IllegalStateException(e);
60+
}
61+
62+
return watched;
63+
}
64+
65+
public List<String> getChildren(final String node, final boolean watch) {
66+
67+
List<String> childNodes = null;
68+
69+
try {
70+
childNodes = zooKeeper.getChildren(node, watch);
71+
} catch (KeeperException | InterruptedException e) {
72+
throw new IllegalStateException(e);
73+
}
74+
75+
return childNodes;
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
3+
<log4j:configuration debug="true"
4+
xmlns:log4j='http://jakarta.apache.org/log4j/'>
5+
6+
<appender name="console" class="org.apache.log4j.ConsoleAppender">
7+
<layout class="org.apache.log4j.PatternLayout">
8+
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
9+
</layout>
10+
</appender>
11+
12+
<logger name="org.apache.zookeeper.ZooKeeper">
13+
<level value="warn" />
14+
</logger>
15+
16+
<root>
17+
<level value="DEBUG" />
18+
<appender-ref ref="console" />
19+
</root>
20+
21+
</log4j:configuration>

0 commit comments

Comments
 (0)