-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathCluster.java
More file actions
107 lines (94 loc) · 4.15 KB
/
Copy pathCluster.java
File metadata and controls
107 lines (94 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package server.internal;
import commons.FluxExecutor;
import grpc.BrokerServer;
import metadata.Metadata;
import org.tinylog.Logger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Cluster {
private String clusterId; // ex: CLUSTER-1
private List<Broker> nodes;
private Broker controllerNode;
public Cluster(String clusterId) {
this.clusterId = clusterId;
this.nodes = new ArrayList<>();
}
// Initializes all the brokers to be in this cluster, but does not yet start them up.
public void bootstrapCluster(List<InetSocketAddress> bootstrapServerAddrs) throws IOException {
// for each address, create a broker @ that particular address
for (int i = 0; i < bootstrapServerAddrs.size(); i++) {
Broker broker = new Broker(
"BROKER-%d".formatted(Metadata.brokerIdCounter.getAndIncrement()),
bootstrapServerAddrs.get(i).getHostName(),
bootstrapServerAddrs.get(i).getPort()
);
nodes.add(broker);
}
// Pick first node by default since we currently don't have a consensus algo implemented rn
controllerNode = nodes.get(0);
controllerNode.setIsActiveController(true);
String controllerEndpoint = "%s:%d".formatted(bootstrapServerAddrs.get(0).getHostName(), bootstrapServerAddrs.get(0).getPort());
controllerNode.setControllerEndpoint(controllerEndpoint);
controllerNode.setClusterId(this.clusterId);
if (nodes.size() > 1) {
// All other nodes must then store the controller node's endpoint in order to make further requests,
// i.e., broker registration, heartbeats, etc
List<Broker> followerNodes = nodes.subList(1, nodes.size());
for (Broker node : followerNodes) {
node.setControllerEndpoint(controllerNode.getControllerEndpoint());
}
}
controllerNode.initControllerBrokerMetadata();
}
// Fire up each server, ready for requests.
public void startCluster() {
// Must fire up Controller node first so that it's ready to accept requests immediately
CountDownLatch latch = new CountDownLatch(1);
FluxExecutor.getExecutorService().submit(() -> {
BrokerServer controllerServer = new BrokerServer(controllerNode);
try {
controllerServer.start(controllerNode.getPort());
latch.countDown();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
try {
// The thread will block until our latch gets counted down to 0 (which only happens after the controller starts up).
// By doing this, we can ensure the controller is ready before sending any requests.
if (latch.await(10, TimeUnit.SECONDS)) {
for (Broker b : nodes) {
if (!b.isActiveController()) {
// Start up each broker in its own thread
FluxExecutor.getExecutorService().submit(() -> {
BrokerServer server = new BrokerServer(b);
try {
server.start(b.getPort());
// Once each server is started, it will immediately make a BrokerRegistrationRequest to the controller
b.registerBroker(); // initial metadata state is handled here too
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public String getClusterId() {
return clusterId;
}
public List<Broker> getNodes() {
return nodes;
}
public Broker getControllerNode() {
return controllerNode;
}
}