66import java .net .http .HttpResponse ;
77import java .time .Duration ;
88import java .util .ArrayList ;
9+ import java .util .HashSet ;
910import java .util .LinkedHashMap ;
1011import java .util .List ;
1112import java .util .Map ;
13+ import java .util .Set ;
1214import java .util .concurrent .ConcurrentHashMap ;
1315import java .util .logging .Logger ;
1416
@@ -25,12 +27,14 @@ static ClusterRuntime get() {
2527 private volatile Config cfg ;
2628 private volatile boolean running ;
2729 private volatile Thread worker ;
30+ private volatile String lastStateDigest = "" ;
2831
2932 private ClusterRuntime () {}
3033
3134 void start (Config cfg ) {
3235 this .cfg = cfg ;
3336 registerSelf ();
37+ log .info ("cluster start: node=" + cfg .clusterNodeId () + ", listen=" + cfg .clusterListen () + ", peers=" + cfg .clusterPeers ());
3438 if (running || cfg .clusterPeers ().isEmpty ()) return ;
3539 running = true ;
3640 worker = new Thread (this ::loop , "cluster-runtime" );
@@ -124,6 +128,7 @@ private void pullPeers() {
124128 markPeerDown (u );
125129 }
126130 }
131+ logClusterState (c );
127132 }
128133
129134 private HttpRequest clusterPeerGet (URI uri ) {
@@ -160,6 +165,72 @@ private void markPeerDown(String endpoint) {
160165 }
161166 }
162167
168+ private void logClusterState (Config c ) {
169+ List <ClusterNode > copy = new ArrayList <>(nodes .values ());
170+ List <String > online = new ArrayList <>();
171+ for (ClusterNode n : copy ) {
172+ if (!n .alive ) continue ;
173+ String ep = normalizeEndpoint (n .endpoint );
174+ if (ep .isBlank ()) {
175+ online .add (n .nodeId );
176+ } else {
177+ online .add (n .nodeId + "(" + ep + ")" );
178+ }
179+ }
180+ online .sort (String ::compareTo );
181+
182+ Set <String > onlineEndpoints = new HashSet <>();
183+ for (ClusterNode n : copy ) {
184+ if (!n .alive ) continue ;
185+ String ep = normalizeEndpoint (n .endpoint );
186+ if (!ep .isBlank ()) onlineEndpoints .add (ep );
187+ }
188+
189+ List <String > waiting = new ArrayList <>();
190+ for (String raw : c .clusterPeers ()) {
191+ String ep = normalizeEndpoint (raw );
192+ if (ep .isBlank ()) continue ;
193+ if (!onlineEndpoints .contains (ep )) waiting .add (ep );
194+ }
195+ waiting .sort (String ::compareTo );
196+
197+ int total = 1 + c .clusterPeers ().size ();
198+ int connected = total - waiting .size ();
199+ String digest = "connected=" + connected + "/" + total + "|online=" + online + "|waiting=" + waiting ;
200+ if (digest .equals (lastStateDigest )) return ;
201+ lastStateDigest = digest ;
202+
203+ if (waiting .isEmpty ()) {
204+ log .info ("cluster ready: connected " + connected + "/" + total + ", online " + online );
205+ return ;
206+ }
207+ log .info ("cluster sync: connected " + connected + "/" + total + ", online " + online + ", waiting " + waiting );
208+ }
209+
210+ private static String normalizeEndpoint (String raw ) {
211+ if (raw == null ) return "" ;
212+ String s = raw .trim ();
213+ if (s .isEmpty ()) return "" ;
214+ try {
215+ String u = s ;
216+ if (!u .startsWith ("http://" ) && !u .startsWith ("https://" )) {
217+ u = "http://" + u ;
218+ }
219+ URI uri = URI .create (u );
220+ String host = uri .getHost ();
221+ int port = uri .getPort ();
222+ if (host == null || host .isBlank ()) {
223+ return s ;
224+ }
225+ if (port <= 0 ) {
226+ return host ;
227+ }
228+ return host + ":" + port ;
229+ } catch (Exception ignored ) {
230+ return s ;
231+ }
232+ }
233+
163234 private void mergeFromJson (String raw ) {
164235 if (raw == null || raw .isBlank ()) return ;
165236 List <Map <String , String >> parsed = parseNodes (raw );
0 commit comments