Einführung in Atomix

1. Überblick

Die meisten verteilten Anwendungen erfordern einige zustandsbehaftete Komponenten, um konsistent und fehlertolerant zu sein. Atomix ist eine einbettbare Bibliothek, mit der Fehlertoleranz und Konsistenz für verteilte Ressourcen erreicht werden können.

Es bietet eine Vielzahl von APIs zur Verwaltung seiner Ressourcen wie Sammlungen, Gruppen und Tools für Parallelität.

Um zu beginnen, müssen wir die folgende Maven-Abhängigkeit in unser Pom aufnehmen:

<dependency>
    <groupId>io.atomix</groupId>
    <artifactId>atomix-all</artifactId>
    <version>1.0.8</version>
</dependency>

Diese Abhängigkeit stellt einen Netty-basierten Transport bereit, den die Knoten für die Kommunikation miteinander benötigen.

2. Bootstrapping eines Clusters

Um mit Atomix beginnen zu können, müssen Sie zuerst einen Cluster bootstrappen.

Atomix besteht aus einer Reihe von Repliken, die zum Erstellen von Stateful-Distributed-Ressourcen verwendet werden. Jedes Replikat enthält eine Kopie des Status aller im Cluster vorhandenen Ressourcen.

Replikate sind zwei Typen in einem Cluster: aktiv und passiv.

Zustandsänderungen verteilter Ressourcen werden durch aktive Replikate propagiert, während passive Replikate synchron gehalten werden, um die Fehlertoleranz aufrecht zu erhalten.

2.1. Bootstrapping eines eingebetteten Clusters

Um einen Cluster mit einem einzelnen Knoten zu booten, müssen Sie zuerst eine Instanz von AtomixReplica erstellen:

AtomixReplica replica = AtomixReplica.builder(
  new Address("localhost", 8700))
   .withStorage(storage)
   .withTransport(new NettyTransport())
   .build();

Hier wird das Replikat mit Storage und Transport konfiguriert. Code-Snippet zur Speicherdeklaration:

Storage storage = Storage.builder()
  .withDirectory(new File("logs"))
  .withStorageLevel(StorageLevel.DISK)
  .build();

Sobald das Replikat für Speicher und Transport deklariert und konfiguriert ist, können Sie es bootstrapen, indem Sie einfach bootstrap () aufrufen. Dies gibt ein CompletableFuture zurück, das zum Blockieren verwendet werden kann, bis der Server durch den Aufruf der zugehörigen blockierenden join () -Methode gebootet wird:

CompletableFuture<AtomixReplica> future = replica.bootstrap();
future.join();
  • Bisher haben wir einen einzelnen Knotencluster erstellt. Jetzt können wir weitere Knoten hinzufügen. **

Dazu müssen wir weitere Replikate erstellen und mit dem vorhandenen Cluster verknüpfen. Wir müssen einen neuen Thread erzeugen, um die join (Address) -Methode aufzurufen:

AtomixReplica replica2 = AtomixReplica.builder(
  new Address("localhost", 8701))
    .withStorage(storage)
    .withTransport(new NettyTransport())
    .build();

replica2
  .join(new Address("localhost", 8700))
  .join();

AtomixReplica replica3 = AtomixReplica.builder(
  new Address("localhost", 8702))
    .withStorage(storage)
    .withTransport(new NettyTransport())
    .build();

replica3.join(
  new Address("localhost", 8700),
  new Address("localhost", 8701))
  .join();

Jetzt haben wir einen Cluster mit drei Knoten, der bootstrapped ist. Alternativ können Sie einen Cluster bootstrappen, indem Sie eine List von Adressen in der Methode bootstrap (List <Address>) übergeben:

List<Address> cluster = Arrays.asList(
  new Address("localhost", 8700),
  new Address("localhost", 8701),
  new Address("localhsot", 8702));

AtomixReplica replica1 = AtomixReplica
  .builder(cluster.get(0))
  .build();
replica1.bootstrap(cluster).join();

AtomixReplica replica2 = AtomixReplica
  .builder(cluster.get(1))
  .build();

replica2.bootstrap(cluster).join();

AtomixReplica replica3 = AtomixReplica
  .builder(cluster.get(2))
  .build();

replica3.bootstrap(cluster).join();

Für jede Replik müssen wir einen neuen Thread erzeugen.

2.2. Bootstrapping eines Standalone-Clusters

Der Atomix-Server kann als Standalone-Server ausgeführt werden, der von Maven Central heruntergeladen werden kann. Einfach ausgedrückt - es handelt sich um ein Java-Archiv, das über das Terminal ausgeführt werden kann

Einfach ausgedrückt - es handelt sich um ein Java-Archiv, das über das Terminal ausgeführt werden kann, indem der Parameter host: port im Adress-Flag und das -bootstrap -Flag angegeben werden.

Hier ist der Befehl zum Booten eines Clusters:

java -jar atomix-standalone-server.jar
  -address 127.0.0.1:8700 -bootstrap -config atomix.properties

Hier ist atomix.properties die Konfigurationsdatei zum Konfigurieren von Speicher und Transport. Um einen Multinode-Cluster zu erstellen, können Sie mit dem -join flag Knoten zum vorhandenen Cluster hinzufügen.

Das Format dafür ist:

java -jar atomix-standalone-server.jar
  -address 127.0.0.1:8701 -join 127.0.0.1:8700

3. Mit einem Client arbeiten

Atomix unterstützt die Erstellung eines Clients für den Remote-Zugriff auf seinen Cluster über die AtomixClient -API.

Da Clients nicht stateful sein müssen, verfügt AtomixClient über keinen Speicher. Beim Erstellen des Clients müssen Sie lediglich den Transport konfigurieren, da der Transport für die Kommunikation mit einem Cluster verwendet wird.

Erstellen wir einen Kunden mit einem Transport:

AtomixClient client = AtomixClient.builder()
  .withTransport(new NettyTransport())
  .build();

Wir müssen jetzt den Client mit dem Cluster verbinden.

Wir können eine List of Address deklarieren und die List als Argument an die connect () -Methode des Clients übergeben:

client.connect(cluster)
  .thenRun(() -> {
      System.out.println("Client is connected to the cluster!");
  });

4. Umgang mit Ressourcen

Die wahre Stärke von Atomix liegt in seinen starken APIs zur Erstellung und Verwaltung verteilter Ressourcen. Ressourcen werden in einem Cluster repliziert und persistiert und durch einen replizierten Zustandsautomaten unterstützt, der von der zugrunde liegenden Implementierung des Raft Consensus Protocol verwaltet wird.

Verteilte Ressourcen können mit einer ihrer get () - Methode erstellt und verwaltet werden. Wir können eine verteilte Ressourceninstanz aus AtomixReplica erstellen.

Replica ist eine Instanz von AtomixReplica , dem Code-Snippet zum Erstellen einer verteilten Kartenressource und zum Festlegen eines Werts:

replica.getMap("map")
  .thenCompose(m -> m.put("bar", "Hello world!"))
  .thenRun(() -> System.out.println("Value is set in Distributed Map"))
  .join();

Hier blockiert die join () - Methode das Programm, bis die Ressource erstellt und der Wert auf sie gesetzt ist. Wir können dasselbe Objekt mit AtomixClient abrufen und den Wert mit der get ("bar") - Methode abrufen.

Wir können am Ende die Methode get () verwenden, um auf das Ergebnis zu warten:

String value = client.getMap("map"))
  .thenCompose(m -> m.get("bar"))
  .thenApply(a -> (String) a)
  .get();

5. Konsistenz und Fehlertoleranz

Atomix wird für unternehmenskritische kleine Datensätze verwendet, bei denen Konsistenz ein größeres Problem ist als die Verfügbarkeit.

  • Es bietet eine stark konfigurierbare Konsistenz durch Linearisierbarkeit für Lese- und Schreibvorgänge ** . In der Linearisierbarkeit wissen alle Clients, sobald ein Schreibvorgang ausgeführt wurde, der resultierende Status.

Die Konsistenz im Atomix-Cluster wird durch den zugrundeliegenden Raft-Konsensalgorithmus garantiert, bei dem ein gewählter Anführer alle Schreibvorgänge erhält, die zuvor erfolgreich waren.

Alle neuen Schreibvorgänge werden durch den Cluster-Leader geleitet und vor der Fertigstellung synchron auf einer Mehrheit des Servers repliziert.

Um die Fehlertoleranz aufrechtzuerhalten, muss der Majoritätsserver des Clusters aktiv sein . Wenn die Anzahl der Knoten in einer Minderheit ausfällt, werden die Knoten als inaktiv markiert und durch passive oder Standby-Knoten ersetzt.

Bei einem Leader-Ausfall beginnen die verbleibenden Server im Cluster mit einer neuen Leader-Wahl. Inzwischen ist der Cluster nicht verfügbar.

Wenn sich ein Anführer auf der Nicht-Quorum-Seite der Partition befindet, wird er zurückgesetzt, und ein neuer Anführer wird auf der Seite mit einem Quorum gewählt.

Wenn sich der Führer auf der Mehrheitsseite befindet, wird er unverändert weitergeführt. Wenn die Partition aufgelöst ist, werden Knoten auf der Nicht-Quorumseite dem Quorum beitreten und ihr Protokoll entsprechend aktualisieren.

6. Fazit

Wie ZooKeeper bietet Atomix eine Reihe von Bibliotheken für verteilte Berechnungen.

Und wie immer ist der vollständige Quellcode für diese Aufgabe verfügbar: over auf GitHub .