Commit fc503baf authored by Mohamad Bashar Desoki's avatar Mohamad Bashar Desoki

Cluster Management, Registration and Discovery

parents
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
\ No newline at end of file
# Cluster Management, Registration and Discovery
*topics*
* [Logback vs SLF4J vs Log4J2 - what is the difference?](https://www.youtube.com/watch?v=SWHYrCXIL38&ab_channel=JavaBrains)
* Introduction to Service Registry & Service Discovery
* Implement Service Registry with Zookeeper
* Integrate into our leader-worker architecture
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>org.example</groupId>
<artifactId>Service-Registration-and-Discovery</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.9.1</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class Application implements Watcher {
private static final String address = "172.29.3.101:2181";
private static final int SESSION_TIMEOUT = 3000; //dead client
private static final int DEFAULT_PORT = 8080;
private ZooKeeper zooKeeper;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
int currentServerPort = args.length == 1 ? Integer.parseInt(args[0]) : DEFAULT_PORT;
Application application = new Application();
ZooKeeper zooKeeper = application.connectToZookeeper();
ServiceRegistry serviceRegistry = new ServiceRegistry(zooKeeper);
OnElectionAction onElectionAction = new OnElectionAction(serviceRegistry, currentServerPort);
LeaderElection leaderElection = new LeaderElection(zooKeeper, onElectionAction);
leaderElection.volunteerForLeadership();
leaderElection.reelectLeader();
application.run();
application.close();
}
public ZooKeeper connectToZookeeper() throws IOException {
this.zooKeeper = new ZooKeeper(address, SESSION_TIMEOUT, this);
return zooKeeper;
}
public void run() throws InterruptedException {
synchronized (zooKeeper) {
zooKeeper.wait();
}
}
private void close() throws InterruptedException {
this.zooKeeper.close();
}
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case None:
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("Successfully connected to Zookeeper");
} else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
synchronized (zooKeeper) {
System.out.println("Disconnected from Zookeeper");
zooKeeper.notifyAll();
}
} else if (watchedEvent.getState() == Event.KeeperState.Closed) {
System.out.println("Closed Successfully");
}
break;
}
}
}
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.Collections;
import java.util.List;
public class LeaderElection implements Watcher {
private static final String ELECTION_NAMESPACE = "/election";
private String currentZnodeName;
private ZooKeeper zooKeeper;
private OnElectionCallback onElectionCallback;
public LeaderElection(ZooKeeper zooKeeper, OnElectionCallback onElectionCallback) {
this.zooKeeper = zooKeeper;
this.onElectionCallback = onElectionCallback;
}
public void volunteerForLeadership() throws InterruptedException, KeeperException {
String znodePrefix = ELECTION_NAMESPACE + "/c_";
String znodeFullPath = zooKeeper.create(znodePrefix, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(znodeFullPath);
this.currentZnodeName = znodeFullPath.replace(ELECTION_NAMESPACE + "/", "");
}
public void reelectLeader() throws InterruptedException, KeeperException {
String predecessorName = "";
Stat predecessorStat = null;
//this while to guarantee get predecessor even if it deleted just before zookeeper.exist
while (predecessorStat == null) {
List<String> children = zooKeeper.getChildren(ELECTION_NAMESPACE, false);
Collections.sort(children);
String smallestChild = children.get(0); //the first element
if (smallestChild.equals(currentZnodeName)) {
System.out.println("I'm a leader");
onElectionCallback.onElectedToBeLeader();
return;
} else {
System.out.println("I'm not a leader");
int predecessorIndex = children.indexOf(currentZnodeName) - 1;
predecessorName = children.get(predecessorIndex);
predecessorStat = zooKeeper.exists(ELECTION_NAMESPACE + "/" + predecessorName, this);
}
}
onElectionCallback.onWorker();
System.out.println("Watching znode " + predecessorName);
System.out.println();
}
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case NodeDeleted:
try {
reelectLeader();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (KeeperException e) {
throw new RuntimeException(e);
}
break;
}
}
}
import org.apache.zookeeper.KeeperException;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class OnElectionAction implements OnElectionCallback {
private final ServiceRegistry serviceRegistry;
private final int port;
public OnElectionAction(ServiceRegistry serviceRegistry, int port) {
this.serviceRegistry = serviceRegistry;
this.port = port;
}
@Override
public void onElectedToBeLeader() {
serviceRegistry.unregisterFromCluster();
serviceRegistry.registerForUpdates();
}
@Override
public void onWorker() {
try {
String currentServerAddress =
String.format("http://%s:%d", InetAddress.getLocalHost().getCanonicalHostName(), port);
serviceRegistry.registerToCluster(currentServerAddress);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
}
public interface OnElectionCallback {
void onElectedToBeLeader();
void onWorker();
}
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ServiceRegistry implements Watcher {
private static final String REGISTRY_ZNODE = "/service_registry";
private final ZooKeeper zooKeeper;
private String currentZnode = null;
private List<String> allServiceAddresses = null;
public ServiceRegistry(ZooKeeper zooKeeper) {
this.zooKeeper = zooKeeper;
createServiceRegistryZnode();
}
private void createServiceRegistryZnode() {
try {
if (zooKeeper.exists(REGISTRY_ZNODE, false) == null) {
zooKeeper.create(REGISTRY_ZNODE, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void registerToCluster(String metadata) throws KeeperException, InterruptedException {
if (this.currentZnode != null) {
System.out.println("Already registered to service registry");
return;
}
this.currentZnode = zooKeeper.create(REGISTRY_ZNODE + "/n_", metadata.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("Registered to service registry");
}
public void registerForUpdates() {
try {
updateAddresses();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void unregisterFromCluster() {
try {
if (currentZnode != null && zooKeeper.exists(currentZnode, false) != null) {
zooKeeper.delete(currentZnode, -1);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void updateAddresses() throws KeeperException, InterruptedException {
List<String> workerZnodes = zooKeeper.getChildren(REGISTRY_ZNODE, this);
List<String> addresses = new ArrayList<>(workerZnodes.size());
for (String workerZnode : workerZnodes) {
String workerFullPath = REGISTRY_ZNODE + "/" + workerZnode;
Stat stat = zooKeeper.exists(workerFullPath, false);
if (stat == null) {
continue;
}
byte[] addressBytes = zooKeeper.getData(workerFullPath, false, stat);
String address = new String(addressBytes);
addresses.add(address);
}
this.allServiceAddresses = Collections.unmodifiableList(addresses);
System.out.println("The cluster addresses are: " + this.allServiceAddresses);
}
@Override
public void process(WatchedEvent watchedEvent) {
try {
updateAddresses();
} catch (KeeperException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="WARN">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment