|
58 | 58 | import java.util.Collection;
|
59 | 59 | import java.util.Collections;
|
60 | 60 | import java.util.HashMap;
|
| 61 | +import java.util.HashSet; |
61 | 62 | import java.util.List;
|
62 | 63 | import java.util.Map;
|
63 | 64 | import java.util.Map.Entry;
|
64 | 65 | import java.util.Optional;
|
65 | 66 | import java.util.Properties;
|
| 67 | +import java.util.Set; |
66 | 68 | import java.util.concurrent.CompletableFuture;
|
67 | 69 | import java.util.concurrent.ExecutionException;
|
68 | 70 | import java.util.concurrent.ExecutorService;
|
@@ -314,8 +316,9 @@ private static void setupNodeDirectories(File baseDirectory,
|
314 | 316 | private final Map<Integer, BrokerServer> brokers;
|
315 | 317 | private final File baseDirectory;
|
316 | 318 | private final SimpleFaultHandlerFactory faultHandlerFactory;
|
317 |
| - private final PreboundSocketFactoryManager socketFactoryManager; |
| 319 | + private PreboundSocketFactoryManager socketFactoryManager; |
318 | 320 | private final String controllerListenerName;
|
| 321 | + private Map<Integer, Set<String>> nodeIdToListeners = new HashMap<>(); |
319 | 322 |
|
320 | 323 | private KafkaClusterTestKit(
|
321 | 324 | TestKitNodes nodes,
|
@@ -437,6 +440,130 @@ public void startup() throws ExecutionException, InterruptedException {
|
437 | 440 | }
|
438 | 441 | }
|
439 | 442 |
|
| 443 | + public void shutdown() throws Exception { |
| 444 | + List<Entry<String, Future<?>>> futureEntries = new ArrayList<>(); |
| 445 | + try { |
| 446 | + // Note the shutdown order here is chosen to be consistent with |
| 447 | + // `KafkaRaftServer`. See comments in that class for an explanation. |
| 448 | + for (Entry<Integer, BrokerServer> entry : brokers.entrySet()) { |
| 449 | + int brokerId = entry.getKey(); |
| 450 | + BrokerServer broker = entry.getValue(); |
| 451 | + nodeIdToListeners.computeIfAbsent(brokerId, __ -> new HashSet<>()); |
| 452 | + Set<String> listeners = nodeIdToListeners.get(brokerId); |
| 453 | + broker.socketServer().dataPlaneAcceptors().forEach((endpoint, acceptor) -> { |
| 454 | + listeners.add(endpoint.listenerName().value() + "://" + endpoint.host() + ":" + acceptor.localPort()); |
| 455 | + }); |
| 456 | + if (!broker.socketServer().controlPlaneAcceptorOpt().isEmpty()) { |
| 457 | + listeners.add(broker.socketServer().controlPlaneAcceptorOpt().get().endPoint().listenerName().value() + "://" + |
| 458 | + broker.socketServer().controlPlaneAcceptorOpt().get().endPoint().host() + ":" + |
| 459 | + broker.socketServer().controlPlaneAcceptorOpt().get().localPort()); |
| 460 | + } |
| 461 | + nodeIdToListeners.put(brokerId, listeners); |
| 462 | + futureEntries.add(new SimpleImmutableEntry<>("broker" + brokerId, |
| 463 | + executorService.submit((Runnable) broker::shutdown))); |
| 464 | + } |
| 465 | + waitForAllFutures(futureEntries); |
| 466 | + futureEntries.clear(); |
| 467 | + for (Entry<Integer, ControllerServer> entry : controllers.entrySet()) { |
| 468 | + int controllerId = entry.getKey(); |
| 469 | + ControllerServer controller = entry.getValue(); |
| 470 | + nodeIdToListeners.computeIfAbsent(controllerId, __ -> new HashSet<>()); |
| 471 | + Set<String> listeners = nodeIdToListeners.get(controllerId); |
| 472 | + controller.socketServer().dataPlaneAcceptors().forEach((endpoint, acceptor) -> { |
| 473 | + listeners.add(endpoint.listenerName().value() + "://" + endpoint.host() + ":" + acceptor.localPort()); |
| 474 | + }); |
| 475 | + if (!controller.socketServer().controlPlaneAcceptorOpt().isEmpty()) { |
| 476 | + listeners.add(controller.socketServer().controlPlaneAcceptorOpt().get().endPoint().listenerName().value() + "://" + |
| 477 | + controller.socketServer().controlPlaneAcceptorOpt().get().endPoint().host() + ":" + |
| 478 | + controller.socketServer().controlPlaneAcceptorOpt().get().localPort()); |
| 479 | + } |
| 480 | + nodeIdToListeners.put(controllerId, listeners); |
| 481 | + futureEntries.add(new SimpleImmutableEntry<>("controller" + controllerId, |
| 482 | + executorService.submit(controller::shutdown))); |
| 483 | + } |
| 484 | + waitForAllFutures(futureEntries); |
| 485 | + futureEntries.clear(); |
| 486 | + socketFactoryManager.close(); |
| 487 | + } catch (Exception e) { |
| 488 | + for (Entry<String, Future<?>> entry : futureEntries) { |
| 489 | + entry.getValue().cancel(true); |
| 490 | + } |
| 491 | + throw e; |
| 492 | + } |
| 493 | + } |
| 494 | + |
| 495 | + public void restart(Map<Integer, Map<String, Object>> perServerOverriddenConfig) throws Exception { |
| 496 | + shutdown(); |
| 497 | + |
| 498 | + Map<Integer, SharedServer> jointServers = new HashMap<>(); |
| 499 | + |
| 500 | + socketFactoryManager = new PreboundSocketFactoryManager(); |
| 501 | + controllers.forEach((id, controller) -> { |
| 502 | + Map<String, Object> config = controller.config().originals(); |
| 503 | + config.putAll(perServerOverriddenConfig.getOrDefault(-1, Collections.emptyMap())); |
| 504 | + config.putAll(perServerOverriddenConfig.getOrDefault(id, Collections.emptyMap())); |
| 505 | + config.put(SocketServerConfigs.LISTENERS_CONFIG, String.join(",", nodeIdToListeners.get(id))); |
| 506 | + |
| 507 | + TestKitNode node = nodes.controllerNodes().get(id); |
| 508 | + KafkaConfig nodeConfig = new KafkaConfig(config, false); |
| 509 | + SharedServer sharedServer = new SharedServer( |
| 510 | + nodeConfig, |
| 511 | + node.initialMetaPropertiesEnsemble(), |
| 512 | + Time.SYSTEM, |
| 513 | + new Metrics(), |
| 514 | + CompletableFuture.completedFuture(QuorumConfig.parseVoterConnections(nodeConfig.quorumConfig().voters())), |
| 515 | + Collections.emptyList(), |
| 516 | + faultHandlerFactory, |
| 517 | + socketFactoryManager.getOrCreateSocketFactory(node.id()) |
| 518 | + ); |
| 519 | + try { |
| 520 | + controller = new ControllerServer( |
| 521 | + sharedServer, |
| 522 | + KafkaRaftServer.configSchema(), |
| 523 | + nodes.bootstrapMetadata()); |
| 524 | + } catch (Throwable e) { |
| 525 | + log.error("Error creating controller {}", node.id(), e); |
| 526 | + Utils.swallow(log, Level.WARN, "sharedServer.stopForController error", sharedServer::stopForController); |
| 527 | + throw e; |
| 528 | + } |
| 529 | + controllers.put(node.id(), controller); |
| 530 | + jointServers.put(node.id(), sharedServer); |
| 531 | + }); |
| 532 | + |
| 533 | + brokers.forEach((id, broker) -> { |
| 534 | + Map<String, Object> config = broker.config().originals(); |
| 535 | + config.putAll(perServerOverriddenConfig.getOrDefault(-1, Collections.emptyMap())); |
| 536 | + config.putAll(perServerOverriddenConfig.getOrDefault(id, Collections.emptyMap())); |
| 537 | + config.put(SocketServerConfigs.LISTENERS_CONFIG, String.join(",", nodeIdToListeners.get(id))); |
| 538 | + |
| 539 | + TestKitNode node = nodes.brokerNodes().get(id); |
| 540 | + KafkaConfig nodeConfig = new KafkaConfig(config); |
| 541 | + SharedServer sharedServer = jointServers.computeIfAbsent( |
| 542 | + node.id(), |
| 543 | + nodeId -> new SharedServer( |
| 544 | + nodeConfig, |
| 545 | + node.initialMetaPropertiesEnsemble(), |
| 546 | + Time.SYSTEM, |
| 547 | + new Metrics(), |
| 548 | + CompletableFuture.completedFuture(QuorumConfig.parseVoterConnections(nodeConfig.quorumConfig().voters())), |
| 549 | + Collections.emptyList(), |
| 550 | + faultHandlerFactory, |
| 551 | + socketFactoryManager.getOrCreateSocketFactory(node.id()) |
| 552 | + ) |
| 553 | + ); |
| 554 | + try { |
| 555 | + broker = new BrokerServer(sharedServer); |
| 556 | + } catch (Throwable e) { |
| 557 | + log.error("Error creating broker {}", node.id(), e); |
| 558 | + Utils.swallow(log, Level.WARN, "sharedServer.stopForBroker error", sharedServer::stopForBroker); |
| 559 | + throw e; |
| 560 | + } |
| 561 | + brokers.put(node.id(), broker); |
| 562 | + }); |
| 563 | + |
| 564 | + startup(); |
| 565 | + } |
| 566 | + |
440 | 567 | /**
|
441 | 568 | * Wait for a controller to mark all the brokers as ready (registered and unfenced).
|
442 | 569 | * And also wait for the metadata cache up-to-date in each broker server.
|
|
0 commit comments