Added LLD projects

This commit is contained in:
Your Name
2020-07-15 18:02:38 +05:30
commit 0d2fabc962
135 changed files with 3319 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
import models.Node;
import models.Request;
import models.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class LoadBalancer {
private final Map<String, Service> services;
private final Map<String, Node> nodes;
public LoadBalancer() {
this.services = new ConcurrentHashMap<>();
this.nodes = new ConcurrentHashMap<>();
}
public void register(Service service) {
services.put(service.getId(), service);
}
public void addNode(String serviceId, Node node) {
nodes.put(node.getId(), node);
services.get(serviceId).getRouter().addNode(node);
}
public void removeNode(String serviceId, String nodeId) {
services.get(serviceId).getRouter().removeNode(nodes.remove(nodeId));
}
public Node getHandler(Request request) {
return services.get(request.getServiceId()).getRouter().getAssignedNode(request);
}
}

View File

@@ -0,0 +1,57 @@
package algorithms;
import models.Node;
import models.Request;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
public class ConsistentHashing implements Router {
private final Map<Node, List<Long>> nodePositions;
private final ConcurrentSkipListMap<Long, Node> nodeMappings;
private final Function<String, Long> hashFunction;
private final int pointMultiplier;
public ConsistentHashing(final Function<String, Long> hashFunction,
final int pointMultiplier) {
if (pointMultiplier == 0) {
throw new IllegalArgumentException();
}
this.pointMultiplier = pointMultiplier;
this.hashFunction = hashFunction;
this.nodePositions = new ConcurrentHashMap<>();
this.nodeMappings = new ConcurrentSkipListMap<>();
}
public void addNode(Node node) {
nodePositions.put(node, new CopyOnWriteArrayList<>());
for (int i = 0; i < pointMultiplier; i++) {
for (int j = 0; j < node.getWeight(); j++) {
final var point = hashFunction.apply((i * pointMultiplier + j) + node.getId());
nodePositions.get(node).add(point);
nodeMappings.put(point, node);
}
}
}
public void removeNode(Node node) {
for (final Long point : nodePositions.remove(node)) {
nodeMappings.remove(point);
}
}
public Node getAssignedNode(Request request) {
final var key = hashFunction.apply(request.getId());
final var entry = nodeMappings.higherEntry(key);
if (entry == null) {
return nodeMappings.firstEntry().getValue();
} else {
return entry.getValue();
}
}
}

View File

@@ -0,0 +1,12 @@
package algorithms;
import models.Node;
import models.Request;
public interface Router {
void addNode(Node node);
void removeNode(Node node);
Node getAssignedNode(Request request);
}

View File

@@ -0,0 +1,47 @@
package algorithms;
import models.Node;
import models.Request;
import java.util.ArrayList;
import java.util.List;
public class WeightedRoundRobin implements Router {
private final List<Node> nodes;
private int assignTo;
private int currentNodeAssignments;
private final Object lock;
public WeightedRoundRobin() {
this.nodes = new ArrayList<>();
this.assignTo = 0;
this.lock = new Object();
}
public void addNode(Node node) {
synchronized (this.lock) {
nodes.add(node);
}
}
public void removeNode(Node node) {
synchronized (this.lock) {
nodes.remove(node);
assignTo--;
currentNodeAssignments = 0;
}
}
public Node getAssignedNode(Request request) {
synchronized (this.lock) {
assignTo = assignTo % nodes.size();
final var currentNode = nodes.get(assignTo);
currentNodeAssignments++;
if (currentNodeAssignments == currentNode.getWeight()) {
assignTo++;
currentNodeAssignments = 0;
}
return currentNode;
}
}
}

View File

@@ -0,0 +1,45 @@
package models;
import java.util.Objects;
public class Node {
private final String id;
private final int weight;
private final String ipAddress;
public Node(String id, String ipAddress) {
this(id, ipAddress, 1);
}
public Node(String id, String ipAddress, int weight) {
this.id = id;
this.weight = weight;
this.ipAddress = ipAddress;
}
public String getId() {
return id;
}
public int getWeight() {
return weight;
}
public String getIpAddress() {
return ipAddress;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
return id.equals(node.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@@ -0,0 +1,26 @@
package models;
public class Request {
private final String id;
private final String serviceId;
private final String method;
public Request(String id, String serviceId, String method) {
this.id = id;
this.serviceId = serviceId;
this.method = method;
}
public String getId() {
return id;
}
public String getServiceId() {
return serviceId;
}
public String getMethod() {
return method;
}
}

View File

@@ -0,0 +1,27 @@
package models;
import algorithms.Router;
public class Service {
private final Router router;
private final String id;
private final String[] methods;
public Service(String id, Router router, String[] methods) {
this.router = router;
this.id = id;
this.methods = methods;
}
public Router getRouter() {
return router;
}
public String getId() {
return id;
}
public String[] getMethods() {
return methods;
}
}

View File

@@ -0,0 +1,70 @@
import algorithms.ConsistentHashing;
import algorithms.WeightedRoundRobin;
import models.Node;
import models.Request;
import models.Service;
import org.junit.Assert;
import org.junit.Test;
public class LBTester {
@Test
public void LBDefaultBehaviour() {
LoadBalancer loadBalancer = new LoadBalancer();
final var consistentHashing = new ConsistentHashing(point -> (long) Math.abs(point.hashCode()) % 100, 1);
final String profileServiceId = "profile", smsServiceId = "sms", emailServiceId = "email";
loadBalancer.register(new Service(profileServiceId, consistentHashing, new String[]{"addProfile", "deleteProfile", "updateProfile"}));
loadBalancer.register(new Service(smsServiceId, new WeightedRoundRobin(), new String[]{"sendSms", "addTemplate", "getSMSForUser"}));
loadBalancer.register(new Service(emailServiceId, new WeightedRoundRobin(), new String[]{"sendEmail", "addTemplate", "getSMSForUser"}));
final Node pNode1 = new Node("51", "35.45.55.65", 2), pNode2 = new Node("22", "35.45.55.66", 3);
loadBalancer.addNode(profileServiceId, pNode1);
loadBalancer.addNode(profileServiceId, pNode2);
final Node sNode1 = new Node("13", "35.45.55.67"), sNode2 = new Node("64", "35.45.55.68");
loadBalancer.addNode(smsServiceId, sNode1);
loadBalancer.addNode(smsServiceId, sNode2);
final Node eNode1 = new Node("node-35", "35.45.55.69", 2), eNode2 = new Node("node-76", "35.45.55.70");
loadBalancer.addNode(emailServiceId, eNode1);
loadBalancer.addNode(emailServiceId, eNode2);
var profileNode1 = loadBalancer.getHandler(new Request("r-123", profileServiceId, "addProfile"));
var profileNode2 = loadBalancer.getHandler(new Request("r-244", profileServiceId, "addProfile"));
var profileNode3 = loadBalancer.getHandler(new Request("r-659", profileServiceId, "addProfile"));
var profileNode4 = loadBalancer.getHandler(new Request("r-73", profileServiceId, "addProfile"));
Assert.assertEquals(pNode1, profileNode1);
Assert.assertEquals(pNode1, profileNode2);
Assert.assertEquals(pNode2, profileNode3);
Assert.assertEquals(pNode1, profileNode4);
loadBalancer.removeNode(profileServiceId, pNode1.getId());
profileNode1 = loadBalancer.getHandler(new Request("r-123", profileServiceId, "addProfile"));
profileNode2 = loadBalancer.getHandler(new Request("r-244", profileServiceId, "addProfile"));
profileNode3 = loadBalancer.getHandler(new Request("r-659", profileServiceId, "addProfile"));
profileNode4 = loadBalancer.getHandler(new Request("r-73", profileServiceId, "addProfile"));
Assert.assertEquals(pNode2, profileNode1);
Assert.assertEquals(pNode2, profileNode2);
Assert.assertEquals(pNode2, profileNode3);
Assert.assertEquals(pNode2, profileNode4);
final var smsNode1 = loadBalancer.getHandler(new Request("r-124", smsServiceId, "addTemplate"));
final var smsNode2 = loadBalancer.getHandler(new Request("r-1214", smsServiceId, "addTemplate"));
final var smsNode3 = loadBalancer.getHandler(new Request("r-4", smsServiceId, "addTemplate"));
Assert.assertEquals(sNode1, smsNode1);
Assert.assertEquals(sNode2, smsNode2);
Assert.assertEquals(sNode1, smsNode3);
final var emailNode1 = loadBalancer.getHandler(new Request("r-1232", emailServiceId, "addTemplate"));
final var emailNode2 = loadBalancer.getHandler(new Request("r-4134", emailServiceId, "addTemplate"));
final var emailNode3 = loadBalancer.getHandler(new Request("r-23432", emailServiceId, "addTemplate"));
final var emailNode4 = loadBalancer.getHandler(new Request("r-5345", emailServiceId, "addTemplate"));
Assert.assertEquals(eNode1, emailNode1);
Assert.assertEquals(eNode1, emailNode2);
Assert.assertEquals(eNode2, emailNode3);
Assert.assertEquals(eNode1, emailNode4);
}
}

View File

@@ -0,0 +1,140 @@
import algorithms.ConsistentHashing;
import algorithms.Router;
import algorithms.WeightedRoundRobin;
import models.Node;
import models.Request;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class RouterTester {
String ipAddress = "127.0.0.1", serviceId = "service", method = "method";
@Test
public void defaultRoundRobin() {
final Router router = new WeightedRoundRobin();
final Node node1 = newNode("node-1"), node2 = newNode("node-2"), node3 = newNode("node-3");
router.addNode(node1);
router.addNode(node2);
router.addNode(node3);
Assert.assertEquals(node1, router.getAssignedNode(newRequest("r-123")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("r-124")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("r-125")));
router.removeNode(node1);
Assert.assertEquals(node2, router.getAssignedNode(newRequest("r-125")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("r-126")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("r-127")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("r-128")));
final Node node4 = new Node("node-4", ipAddress, 2);
router.addNode(node4);
Assert.assertEquals(node4, router.getAssignedNode(newRequest("r-129")));
Assert.assertEquals(node4, router.getAssignedNode(newRequest("r-130")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("r-131")));
}
@Test
public void defaultConsistentHashing() {
final List<Long> hashes = new ArrayList<>();
hashes.add(1L);
hashes.add(11L);
hashes.add(21L);
hashes.add(31L);
final Function<String, Long> hashFunction = id -> {
if (id.contains("000000")) {
return hashes.remove(0);
} else {
return Long.parseLong(id);
}
};
final Router router = new ConsistentHashing(hashFunction, 1);
final Node node1 = newNode("1000000"), node2 = newNode("2000000"), node3 = newNode("3000000");
router.addNode(node1);
router.addNode(node2);
router.addNode(node3);
Assert.assertEquals(node1, router.getAssignedNode(newRequest("35")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("5")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("15")));
router.removeNode(node1);
Assert.assertEquals(node2, router.getAssignedNode(newRequest("22")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("12")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("23")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("13")));
final Node node4 = newNode("4000000");
router.addNode(node4);
Assert.assertEquals(node4, router.getAssignedNode(newRequest("25")));
}
@Test(expected = IllegalArgumentException.class)
public void consistentHashingConstruction() {
new ConsistentHashing(Long::valueOf, 0);
}
@Test
public void consistentHashingWithWeights() {
final List<Long> hashes = new ArrayList<>();
hashes.add(1L); // remaining is node 1
hashes.add(21L); // 12 to 21 is for node 1
hashes.add(11L); // 2 to 11 is for node 2
hashes.add(41L); // 32 to 41 is for node 2
hashes.add(31L); // 22 to 31 is for node 3
hashes.add(51L); // 42 to 51 is for node 3 --> 10 points
final Function<String, Long> hashFunction = id -> {
//range should be (0, 60)
if (id.contains("000000")) {
return hashes.remove(0);
} else {
return Long.parseLong(id);
}
};
final Router router = new ConsistentHashing(hashFunction, 2);
final Node node1 = newNode("1000000"), node2 = newNode("2000000"), node3 = newNode("3000000");
router.addNode(node1);
router.addNode(node2);
router.addNode(node3);
Assert.assertEquals(node1, router.getAssignedNode(newRequest("55")));
Assert.assertEquals(node1, router.getAssignedNode(newRequest("15")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("8")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("33")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("28")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("47")));
router.removeNode(node1);
// remaining is node 2
// 12 to 21 is now for node 3
Assert.assertEquals(node2, router.getAssignedNode(newRequest("58")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("12")));
Assert.assertEquals(node3, router.getAssignedNode(newRequest("23")));
Assert.assertEquals(node2, router.getAssignedNode(newRequest("54")));
final Node node4 = newNode("4000000");
hashes.add(6L); // 0 to 6 is for node 4, 52 to remaining is for node 4
hashes.add(26L); // 12 to 26 is for node 4
router.addNode(node4);
Assert.assertEquals(node4, router.getAssignedNode(newRequest("15")));
Assert.assertEquals(node4, router.getAssignedNode(newRequest("59")));
Assert.assertEquals(node4, router.getAssignedNode(newRequest("5")));
}
private Request newRequest(String s) {
return new Request(s, serviceId, method);
}
private Node newNode(String s) {
return new Node(s, ipAddress);
}
}