7
7
* Licensed under the Apache License, Version 2.0 (the "License");
8
8
* you may not use this file except in compliance with the License.
9
9
* You may obtain a copy of the License at
10
- *
10
+ *
11
11
* http://www.apache.org/licenses/LICENSE-2.0
12
- *
12
+ *
13
13
* Unless required by applicable law or agreed to in writing, software
14
14
* distributed under the License is distributed on an "AS IS" BASIS,
15
15
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
23
import org .slf4j .Logger ;
24
24
import org .slf4j .LoggerFactory ;
25
25
import org .teamapps .cluster .message .protocol .*;
26
+ import org .teamapps .commons .event .Event ;
26
27
import org .teamapps .commons .util .collections .ByKeyComparisonResult ;
27
28
import org .teamapps .commons .util .collections .CollectionUtil ;
28
29
import org .teamapps .configuration .Configuration ;
45
46
46
47
public class Cluster implements ClusterServiceRegistry {
47
48
private static final Logger LOGGER = LoggerFactory .getLogger (MethodHandles .lookup ().lookupClass ());
48
-
49
49
public static final String CLUSTER_SERVICE = "clusterService" ;
50
+
51
+ public final Event <ClusterNodeData > onLeaderAvailable = new Event <>();
52
+ public final Event <List <ClusterNodeData >> onAvailableNodesChange = new Event <>();
53
+
50
54
private final ScheduledExecutorService scheduledExecutorService = Executors .newScheduledThreadPool (2 );
51
55
private final ClusterNodeData localNode ;
52
56
private final Map <String , ClusterNode > clusterNodeMap = new ConcurrentHashMap <>();
@@ -57,6 +61,7 @@ public class Cluster implements ClusterServiceRegistry {
57
61
private final File tempDir ;
58
62
private ClusterConfig clusterConfig ;
59
63
private boolean active = true ;
64
+ private ClusterNodeData leaderNode ;
60
65
61
66
private ThreadPoolExecutor taskExecutor = new ThreadPoolExecutor (0 , Integer .MAX_VALUE ,
62
67
60L , TimeUnit .SECONDS ,
@@ -89,9 +94,14 @@ private Cluster(ClusterConfig clusterConfig) {
89
94
localNode = new ClusterNodeData ()
90
95
.setNodeId (clusterConfig .getNodeId () != null && !clusterConfig .getNodeId ().isBlank () ? clusterConfig .getNodeId () : UUID .randomUUID ().toString ())
91
96
.setHost (clusterConfig .getHost ())
92
- .setPort (clusterConfig .getPort ());
97
+ .setPort (clusterConfig .getPort ())
98
+ .setLeaderNode (clusterConfig .isLeaderNode ());
99
+ if (clusterConfig .isLeaderNode ()) {
100
+ leaderNode = localNode ;
101
+ onLeaderAvailable .fire (leaderNode );
102
+ }
93
103
tempDir = createTempDirSave ();
94
- LOGGER .info ("Cluster node [{}]: started" , localNode .getNodeId ());
104
+ LOGGER .info ("Cluster node [{}]: started {} " , localNode .getNodeId (), clusterConfig . isLeaderNode () ? "as leader node" : "" );
95
105
startServerSocket (localNode );
96
106
if (clusterConfig .getPeerNodes () != null ) {
97
107
clusterConfig .getPeerNodes ().stream ()
@@ -130,17 +140,28 @@ private void startServerSocket(ClusterNodeData localNode) {
130
140
131
141
protected synchronized ClusterConnectionResult handleConnectionRequest (ClusterConnectionRequest request , ClusterConnection connection ) {
132
142
ClusterNodeData remoteNode = request .getLocalNode ();
143
+ LOGGER .info ("Cluster node [{}]: connection requested from: {}, {}" , localNode .getNodeId (), request .getLocalNode ().getNodeId (), request .getLocalNode ().getHost ());
133
144
String [] nodeServices = request .getLocalServices ();
134
145
ClusterConnectionResult connectionResult = new ClusterConnectionResult ().setLocalNode (localNode );
146
+ if (request .getLeaderNode () != null ) {
147
+ if (leaderNode != null && !leaderNode .getNodeId ().equals (request .getLeaderNode ().getNodeId ())) {
148
+ LOGGER .error ("Cluster node [{}]: error: connection requested denied from {}, {} - different leader node: {} vs {}" , localNode .getNodeId (), request .getLocalNode ().getNodeId (), request .getLocalNode ().getHost (), leaderNode .getNodeId (), request .getLocalNode ().getNodeId ());
149
+ return connectionResult
150
+ .setAccepted (false );
151
+ } else if (leaderNode == null ) {
152
+ leaderNode = remoteNode ;
153
+ sendLeaderNodeUpdateToPeers ();
154
+ onLeaderAvailable .fire (leaderNode );
155
+ LOGGER .info ("Cluster node [{}]: new leader node: {}" , localNode .getNodeId (), request .getLeaderNode ().getNodeId ());
156
+ }
157
+ }
135
158
ClusterNode clusterNode = clusterNodeMap .get (remoteNode .getNodeId ());
136
- List <ClusterNode > existingPeerNodes = new ArrayList <>(clusterNodeMap .values ())
159
+ List <ClusterNodeData > knownPeers = new ArrayList <>(clusterNodeMap .values ())
137
160
.stream ()
138
- .filter (node -> !node .getNodeData ().getNodeId ().equals (localNode .getNodeId ()))
139
- .filter (node -> node .getNodeData ().getPort () > 0 )
140
- .toList ();
141
- List <ClusterNodeData > knownPeers = existingPeerNodes .stream ()
142
161
.map (ClusterNode ::getNodeData )
143
- .collect (Collectors .toList ());
162
+ .filter (nodeData -> !nodeData .getNodeId ().equals (localNode .getNodeId ()))
163
+ .filter (nodeData -> nodeData .getPort () > 0 )
164
+ .toList ();
144
165
145
166
if (clusterNode == null || !clusterNode .isConnected ()) {
146
167
if (clusterNode == null ) {
@@ -156,6 +177,15 @@ protected synchronized ClusterConnectionResult handleConnectionRequest(ClusterCo
156
177
sendMessageToPeerNodes (new ClusterNewPeerInfo ().setNewPeer (remoteNode ), remoteNode );
157
178
}
158
179
180
+ request .getKnownPeers ().stream ()
181
+ .filter (peer -> peer .getHost () != null )
182
+ .filter (peer -> peer .getPort () > 0 )
183
+ .filter (peer -> !clusterNodeMap .containsKey (peer .getNodeId ()))
184
+ .filter (peer -> !localNode .getNodeId ().equals (peer .getNodeId ()))
185
+ .forEach (this ::connectNode );
186
+
187
+ handleAvailableClusterNodesChanged ();
188
+
159
189
return connectionResult
160
190
.setAccepted (true )
161
191
.setKnownPeers (knownPeers )
@@ -171,6 +201,17 @@ protected synchronized void handleConnectionResult(ClusterConnectionResult resul
171
201
if (result .isAccepted ()) {
172
202
LOGGER .info ("Cluster node [{}]: connection request accepted from: {}, {}" , localNode .getNodeId (), result .getLocalNode ().getNodeId (), result .getLocalNode ().getHost ());
173
203
ClusterNode clusterNode = clusterNodeMap .get (remoteNode .getNodeId ());
204
+ if (result .getLeaderNode () != null ) {
205
+ if (leaderNode == null ) {
206
+ leaderNode = result .getLeaderNode ();
207
+ sendLeaderNodeUpdateToPeers ();
208
+ onLeaderAvailable .fire (leaderNode );
209
+ LOGGER .info ("Cluster node [{}]: new leader node: {}" , localNode .getNodeId (), result .getLeaderNode ().getNodeId ());
210
+ } else if (!leaderNode .getNodeId ().equals (result .getLocalNode ().getNodeId ())) {
211
+ LOGGER .error ("Cluster node [{}]: error: connection result denied from {}, {} - different leader node: {} vs {}" , localNode .getNodeId (), result .getLocalNode ().getNodeId (), result .getLocalNode ().getHost (), leaderNode .getNodeId (), result .getLocalNode ().getNodeId ());
212
+ return ;
213
+ }
214
+ }
174
215
if (clusterNode == null ) {
175
216
clusterNode = new ClusterNode (this , remoteNode , connection );
176
217
clusterNodeMap .put (remoteNode .getNodeId (), clusterNode );
@@ -195,6 +236,7 @@ protected synchronized void handleConnectionResult(ClusterConnectionResult resul
195
236
sendMessageToPeerNodes (servicesUpdate );
196
237
}
197
238
}
239
+ handleAvailableClusterNodesChanged ();
198
240
} else {
199
241
LOGGER .info ("Cluster node [{}]: connection request denied from: {}, {}" , localNode .getNodeId (), result .getLocalNode ().getNodeId (), result .getLocalNode ().getHost ());
200
242
}
@@ -257,6 +299,15 @@ protected void handleClusterNewPeerInfo(ClusterNewPeerInfo newPeerInfo, ClusterN
257
299
}
258
300
}
259
301
302
+ protected void handleClusterNewLeaderInfo (ClusterNewLeaderInfo newLeaderInfo , ClusterNode clusterNode ) {
303
+ if (leaderNode == null ) {
304
+ leaderNode = newLeaderInfo .getLeaderNode ();
305
+ sendLeaderNodeUpdateToPeers ();
306
+ onLeaderAvailable .fire (leaderNode );
307
+ LOGGER .info ("Cluster node [{}]: new leader node: {}" , localNode .getNodeId (), newLeaderInfo .getLeaderNode ().getNodeId ());
308
+ }
309
+ }
310
+
260
311
261
312
protected void handleClusterAvailableServicesUpdate (ClusterAvailableServicesUpdate availableServicesUpdate , ClusterNode clusterNode ) {
262
313
updateClusterNodeServices (clusterNode , availableServicesUpdate .getServices ());
@@ -266,6 +317,20 @@ protected synchronized void handleDisconnect(ClusterNode clusterNode) {
266
317
pendingServiceRequestsMap .values ().stream ()
267
318
.filter (clusterTask -> Objects .equals (clusterTask .getProcessingNodeId (), clusterNode .getNodeData ().getNodeId ()))
268
319
.forEach (this ::executeClusterTask );
320
+ handleAvailableClusterNodesChanged ();
321
+ }
322
+
323
+
324
+ private void handleAvailableClusterNodesChanged () {
325
+ List <ClusterNodeData > availableNodes = getAvailablePeerNodes ();
326
+ onAvailableNodesChange .fire (availableNodes );
327
+ }
328
+
329
+ private List <ClusterNodeData > getAvailablePeerNodes () {
330
+ return clusterNodeMap .values ().stream ()
331
+ .filter (ClusterNode ::isConnected )
332
+ .map (ClusterNode ::getNodeData )
333
+ .toList ();
269
334
}
270
335
271
336
private void handleConfigUpdate (ClusterConfig config ) {
@@ -290,17 +355,53 @@ private synchronized void updateClusterNodeServices(ClusterNode clusterNode, Str
290
355
}
291
356
292
357
protected void connectNode (ClusterNodeData peerNode ) {
293
- new ClusterConnection (this , peerNode , new ArrayList <>(localServices .keySet ()));
358
+ List <ClusterNodeData > knownPeers = new ArrayList <>(clusterNodeMap .values ())
359
+ .stream ()
360
+ .map (ClusterNode ::getNodeData )
361
+ .filter (nodeData -> !nodeData .getNodeId ().equals (localNode .getNodeId ()))
362
+ .filter (nodeData -> nodeData .getPort () > 0 )
363
+ .toList ();
364
+ String [] services = localServices .keySet ().toArray (new String [0 ]);
365
+ ClusterConnectionRequest clusterConnectionRequest = new ClusterConnectionRequest ()
366
+ .setLocalNode (localNode )
367
+ .setLocalServices (services )
368
+ .setLeaderNode (leaderNode )
369
+ .setKnownPeers (knownPeers );
370
+ new ClusterConnection (this , peerNode , clusterConnectionRequest );
294
371
}
295
372
296
373
private synchronized void sendMessageToPeerNodes (Message message , ClusterNodeData ... excludingNodes ) {
297
374
Set <String > excludeSet = excludingNodes == null ? new HashSet <>() : Arrays .stream (excludingNodes ).map (ClusterNodeData ::getNodeId ).collect (Collectors .toSet ());
298
- List <ClusterNode > peerNodes = clusterNodeMap .values ().stream ().filter (node -> node .isConnected () && !excludeSet .contains (node .getNodeData ().getNodeId ())).collect (Collectors .toList ());
375
+ List <ClusterNode > peerNodes = clusterNodeMap .values ()
376
+ .stream ()
377
+ .filter (node -> node .isConnected () && !excludeSet .contains (node .getNodeData ().getNodeId ()))
378
+ .toList ();
299
379
LOGGER .info ("Cluster node [{}]: send to peer nodes: {}, message: {}" , localNode .getNodeId (), peerNodes .size (), message .getMessageDefUuid ());
300
380
peerNodes .forEach (node -> node .writeMessage (message ));
301
381
}
302
382
303
383
384
+ private synchronized void sendLeaderNodeUpdateToPeers () {
385
+ List <ClusterNode > peerNodes = clusterNodeMap .values ()
386
+ .stream ()
387
+ .toList ();
388
+ ClusterNewLeaderInfo clusterNewLeaderInfo = new ClusterNewLeaderInfo ().setLeaderNode (leaderNode );
389
+ peerNodes .forEach (node -> node .writeMessage (clusterNewLeaderInfo ));
390
+ }
391
+
392
+ public void sendMessage (String nodeId , Message message ) {
393
+ ClusterNode clusterNode = clusterNodeMap .get (nodeId );
394
+ if (clusterNode != null ) {
395
+ clusterNode .writeMessage (message );
396
+ }
397
+ }
398
+
399
+ public void sendMessage (List <String > nodeIds , Message message ) {
400
+ for (String nodeId : nodeIds ) {
401
+ sendMessage (nodeId , message );
402
+ }
403
+ }
404
+
304
405
@ Override
305
406
public void registerService (AbstractClusterService clusterService ) {
306
407
LOGGER .info ("Cluster node [{}]: register local service: {}" , localNode .getNodeId (), clusterService .getServiceName ());
@@ -323,8 +424,13 @@ public boolean isServiceAvailable(String serviceName) {
323
424
324
425
@ Override
325
426
public <REQUEST extends Message , RESPONSE extends Message > RESPONSE executeServiceMethod (String serviceName , String method , REQUEST request , PojoObjectDecoder <RESPONSE > responseDecoder ) {
326
- LOGGER .info ("Cluster node: {} - execute service method {}/{}" , localNode .getNodeId (), serviceName , method );
327
- ClusterTask clusterTask = new ClusterTask (serviceName , method , request );
427
+ return executeServiceMethod (null , serviceName , method , request , responseDecoder );
428
+ }
429
+
430
+ @ Override
431
+ public <REQUEST extends Message , RESPONSE extends Message > RESPONSE executeServiceMethod (String clusterNodeId , String serviceName , String method , REQUEST request , PojoObjectDecoder <RESPONSE > responseDecoder ) {
432
+ LOGGER .info ("Cluster node: {} - execute service method {}/{}" + (clusterNodeId != null ? ", on node {}" : "" ), localNode .getNodeId (), serviceName , method , clusterNodeId );
433
+ ClusterTask clusterTask = new ClusterTask (serviceName , method , request , clusterNodeId );
328
434
pendingServiceRequestsMap .put (clusterTask .getTaskId (), clusterTask );
329
435
while (!clusterTask .isFinished ()) {
330
436
clusterTask .startProcessing ();
@@ -345,6 +451,29 @@ public <REQUEST extends Message, RESPONSE extends Message> RESPONSE executeServi
345
451
throw new RuntimeException ("Error: execute cluster service method failed:" + serviceName + ", " + method );
346
452
}
347
453
454
+ @ Override
455
+ public <MESSAGE extends Message > void executeServiceBroadcast (String serviceName , String method , MESSAGE message ) {
456
+ LOGGER .info ("Cluster node: {} - execute service broadcast method {}/{}" , localNode .getNodeId (), serviceName , method );
457
+ List <ClusterNode > clusterNodes = nodesByServiceName .get (serviceName );
458
+ if (clusterNodes != null ) {
459
+ ClusterServiceBroadcastMessage broadcastMessage = new ClusterServiceBroadcastMessage ()
460
+ .setServiceName (serviceName )
461
+ .setMethodName (method )
462
+ .setMessage (message );
463
+ clusterNodes .forEach (node -> node .writeMessage (broadcastMessage ));
464
+ }
465
+ }
466
+
467
+ public void handleServiceBroadcastMessage (ClusterServiceBroadcastMessage broadcastMessage , ClusterNode clusterNode ) {
468
+ String serviceName = broadcastMessage .getServiceName ();
469
+ String method = broadcastMessage .getMethodName ();
470
+ LOGGER .info ("Cluster node [{}]: handle broadcast message from {}: {}/{}" , localNode .getNodeId (), clusterNode .getNodeData ().getNodeId (), serviceName , method );
471
+ AbstractClusterService clusterService = localServices .get (serviceName );
472
+ if (clusterService != null ) {
473
+ taskExecutor .execute (() -> clusterService .handleMessage (method , broadcastMessage .getMessage ()));
474
+ }
475
+ }
476
+
348
477
349
478
private void executeClusterTask (ClusterTask clusterTask ) {
350
479
if (clusterTask .isRetryLimitReached ()) {
@@ -357,7 +486,16 @@ private void executeClusterTask(ClusterTask clusterTask) {
357
486
clusterTask .addExecutionAttempt ();
358
487
359
488
AbstractClusterService localService = localServices .get (clusterTask .getServiceName ());
360
- ClusterNode clusterNode = getBestServiceNode (clusterTask .getServiceName ());
489
+ ClusterNode clusterNode ;
490
+ if (clusterTask .isFixedServiceNode ()) {
491
+ if (localNode .getNodeId ().equals (clusterTask .getFixedServiceNodeId ())) {
492
+ clusterNode = null ;
493
+ } else {
494
+ clusterNode = clusterNodeMap .get (clusterTask .getFixedServiceNodeId ());
495
+ }
496
+ } else {
497
+ clusterNode = getBestServiceNode (clusterTask .getServiceName ());
498
+ }
361
499
if (localService != null && clusterNode != null ) {
362
500
if (getActiveTasks () <= clusterNode .getActiveTasks ()) {
363
501
runLocalClusterTask (localService , clusterTask );
@@ -488,4 +626,12 @@ public synchronized List<String> getClusterNodeServices(ClusterNode clusterNode)
488
626
List <String > services = servicesByNode .get (clusterNode );
489
627
return new ArrayList <>(services == null ? Collections .emptyList () : services );
490
628
}
629
+
630
+ public ClusterNodeData getLeaderNode () {
631
+ return leaderNode ;
632
+ }
633
+
634
+ public boolean isLeaderNode () {
635
+ return leaderNode != null && leaderNode .getNodeId ().equals (localNode .getNodeId ());
636
+ }
491
637
}
0 commit comments