Eine kurze Anleitung zum Apache-Geode

Eine Kurzanleitung zu Apache Geode

1. Überblick

Apache Geode ist ein verteiltes In-Memory-Datenraster, das Caching und Datenberechnung unterstützt.

In diesem Tutorial werden wir die Schlüsselkonzepte von Geode behandeln und einige Codebeispiele mit dem Java-Client durchgehen.

2. Konfiguration

Zuerst müssen wir Apache Geode herunterladen und installieren und diegfsh -Umgebung festlegen. Dazu können wir den Anweisungen inGeode’s official guide folgen.

Und zweitens werden in diesem Lernprogramm einige Dateisystemartefakte erstellt. Wir können sie also isolieren, indem wir ein temporäres Verzeichnis erstellen und von dort aus Dinge starten.

2.1. Installation und Konfiguration

In unserem temporären Verzeichnis müssen wir eineLocator-Instanz starten:

gfsh> start locator --name=locator --bind-address=localhost

Locators are responsible for the coordination between different members of a Geode Cluster,, die wir über JMX weiter verwalten können.

Als nächstes starten wir eineServer-Instanz, um eine oder mehrere DatenRegions zu hosten:

gfsh> start server --name=server1 --server-port=0

Wir setzen die Option–server-port auf 0, damit Geode jeden verfügbaren Port auswählt. Wenn wir dies weglassen, verwendet der Server den Standardport 40404. A server is a configurable member of the Cluster that runs as a long-lived process and is responsible for managing data Regions.

Und schließlich brauchen wir einRegion:

gfsh> create region --name=example --type=REPLICATE

InRegion werden wir letztendlich unsere Daten speichern.

2.2. Nachprüfung

Stellen wir sicher, dass alles funktioniert, bevor wir weitermachen.

Lassen Sie uns zunächst prüfen, ob wir unsereServer und unsereLocator haben:

gfsh> list members
 Name   | Id
------- | ----------------------------------------------------------
server1 | 192.168.0.105(server1:6119):1024
locator | 127.0.0.1(locator:5996:locator):1024 [Coordinator]

Und als nächstes haben wir unsereRegion:

gfsh> describe region --name=example
..........................................................
Name            : example
Data Policy     : replicate
Hosting Members : server1

Non-Default Attributes Shared By Hosting Members

 Type  |    Name     | Value
------ | ----------- | ---------------
Region | data-policy | REPLICATE
       | size        | 0
       | scope       | distributed-ack

Außerdem sollten sich einige Verzeichnisse im Dateisystem unter unserem temporären Verzeichnis mit den Namen "locator" und "server1" befinden.

Mit dieser Ausgabe wissen wir, dass wir bereit sind, weiterzumachen.

3. Maven-Abhängigkeit

Nachdem wir eine laufende Geode haben, schauen wir uns den Client-Code an.

Um mit Geode in unserem Java-Code arbeiten zu können, müssen wir dieApache Geode Java client-Bibliothek zu unserenpom hinzufügen:


     org.apache.geode
     geode-core
     1.6.0

Beginnen wir mit dem einfachen Speichern und Abrufen einiger Daten in einigen Regionen.

4. Einfaches Speichern und Abrufen

Lassen Sie uns zeigen, wie einzelne Werte, Wertestapel sowie benutzerdefinierte Objekte gespeichert werden.

Um mit dem Speichern von Daten in unserer Beispielregion zu beginnen, stellen Sie mithilfe des Locators eine Verbindung her:

@Before
public void connect() {
    this.cache = new ClientCacheFactory()
      .addPoolLocator("localhost", 10334)
        .create();
    this.region = cache.
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("example");
}

4.1. Einzelwerte speichern

Jetzt können wir einfach Daten in unserer Region speichern und abrufen:

@Test
public void whenSendMessageToRegion_thenMessageSavedSuccessfully() {

    this.region.put("A", "Hello");
    this.region.put("B", "example");

    assertEquals("Hello", region.get("A"));
    assertEquals("example", region.get("B"));
}

4.2. Mehrere Werte gleichzeitig speichern

Wir können auch mehrere Werte gleichzeitig speichern, wenn wir beispielsweise versuchen, die Netzwerklatenz zu verringern:

@Test
public void whenPutMultipleValuesAtOnce_thenValuesSavedSuccessfully() {

    Supplier> keys = () -> Stream.of("A", "B", "C", "D", "E");
    Map values = keys.get()
        .collect(Collectors.toMap(Function.identity(), String::toLowerCase));

    this.region.putAll(values);

    keys.get()
        .forEach(k -> assertEquals(k.toLowerCase(), this.region.get(k)));
}

4.3. Benutzerdefinierte Objekte speichern

Zeichenfolgen sind nützlich, aber früher als später müssen wir benutzerdefinierte Objekte speichern.

Stellen wir uns vor, wir haben einen Kundendatensatz, den wir mit dem folgenden Schlüsseltyp speichern möchten:

public class CustomerKey implements Serializable {
    private long id;
    private String country;

    // getters and setters
    // equals and hashcode
}

Und der folgende Werttyp:

public class Customer implements Serializable {
    private CustomerKey key;
    private String firstName;
    private String lastName;
    private Integer age;

    // getters and setters
}

Es gibt ein paar zusätzliche Schritte, um diese zu speichern:

Erstensthey should implement SerializableWährend dies keine strenge Anforderung ist, machen Sie sie zuSerializable,Geode can store them more robustly.

Zweitensthey need to be on our application’s classpath as well as the classpath of our Geode Server.

Um sie in den Klassenpfad des Servers zu bringen, packen wir sie zusammen, beispielsweise mitmvn clean package.

Und dann können wir das resultierende Glas in einem neuenstart server-Befehl referenzieren:

gfsh> stop server --name=server1
gfsh> start server --name=server1 --classpath=../lib/apache-geode-1.0-SNAPSHOT.jar --server-port=0

Wieder müssen wir diese Befehle aus dem temporären Verzeichnis ausführen.

Zuletzt erstellen wir ein neuesRegionmit dem Namen "Beispielkunden" auf demServer mit demselben Befehl, den wir zum Erstellen der Region "Beispiel" verwendet haben:

gfsh> create region --name=example-customers --type=REPLICATE

Im Code werden wir uns wie zuvor an den Locator wenden und den benutzerdefinierten Typ angeben:

@Before
public void connect() {
    // ... connect through the locator
    this.customerRegion = this.cache.
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("example-customers");
}

Und dann können wir unseren Kunden wie bisher lagern:

@Test
public void whenPutCustomKey_thenValuesSavedSuccessfully() {
    CustomerKey key = new CustomerKey(123);
    Customer customer = new Customer(key, "William", "Russell", 35);

    this.customerRegion.put(key, customer);

    Customer storedCustomer = this.customerRegion.get(key);
    assertEquals("William", storedCustomer.getFirstName());
    assertEquals("Russell", storedCustomer.getLastName());
}

5. Regionstypen

In den meisten Umgebungen verfügen wir über mehr als eine Kopie oder mehr als eine Partition unserer Region, abhängig von unseren Anforderungen an den Lese- und Schreibdurchsatz.

Bisher haben wir speicherinterne replizierte Regionen verwendet. Lass uns genauer hinschauen.

5.1. Replizierte Region

Wie der Name schon sagt, testena Replicated Region maintains copies of its data on more than one ServerLet dies.

Fügen Sie aus dergfsh -Sconsole im Arbeitsverzeichnis eine weitereServermit dem Namenserver2 zum Cluster hinzu:

gfsh> start server --name=server2 --classpath=../lib/apache-geode-1.0-SNAPSHOT.jar --server-port=0

Denken Sie daran, dass wir bei der Erstellung von „Beispiel“–type=REPLICATE verwendet haben. Aus diesem Grund istGeode will automatically replicate our data to the new server.

Überprüfen Sie dies, indem Sieserver1: stoppen

gfsh> stop server --name=server1

Führen Sie dann eine kurze Abfrage für die Beispielregion aus.

Wenn die Daten erfolgreich repliziert wurden, erhalten wir die folgenden Ergebnisse zurück:

gfsh> query --query='select e.key from /example.entries e'
Result : true
Limit  : 100
Rows   : 5

Result
------
C
B
A
E
D

Es sieht also so aus, als ob die Replikation erfolgreich war!

Durch Hinzufügen eines Replikats zu unserer Region wird die Datenverfügbarkeit verbessert. Da mehr als ein Server auf Anfragen antworten kann, erhalten wir auch einen höheren Lesedurchsatz.

what if they both crash? Since these are in-memory regions, the data will be lost. Für diesen Zweck können wir stattdessen–type=REPLICATE_PERSISTENT verwenden, das beim Replizieren auch die Daten auf der Festplatte speichert.

5.2. Partitionierte Region

Bei größeren Datensätzen können wir das System besser skalieren, indem wir Geode so konfigurieren, dass eine Region in separate Partitionen oder Buckets aufgeteilt wird.

Erstellen wir eine partitionierteRegionmit dem Namen "Beispiel-partitioniert":

gfsh> create region --name=example-partitioned --type=PARTITION

Fügen Sie einige Daten hinzu:

gfsh> put --region=example-partitioned --key="1" --value="one"
gfsh> put --region=example-partitioned --key="2" --value="two"
gfsh> put --region=example-partitioned --key="3" --value="three"

Und schnell überprüfen:

gfsh> query --query='select e.key, e.value from /example-partitioned.entries e'
Result : true
Limit  : 100
Rows   : 3

key | value
--- | -----
2   | two
1   | one
3   | three

Um zu überprüfen, ob die Daten partitioniert wurden, stoppen wirserver1 erneut und fragen erneut ab:

gfsh> stop server --name=server1
gfsh> query --query='select e.key, e.value from /example-partitioned.entries e'
Result : true
Limit  : 100
Rows   : 1

key | value
--- | -----
2   | two

Wir haben diesmal nur einige der Dateneinträge zurückbekommen, da dieser Server nur eine Partition der Daten hat. Alsserver1 gelöscht wurde, gingen seine Daten verloren.

But what if we need both partitioning and redundancy? Geode unterstützt aucha number of other types. Die folgenden drei sind praktisch:

  • PARTITION_REDUNDANT Partitionenand repliziert unsere Daten über verschiedene Mitglieder des Clusters

  • PARTITION_PERSISTENT partitioniert die Daten wiePARTITION, jedoch auf die Festplatte und

  • PARTITION_REDUNDANT_PERSISTENT gibt uns alle drei Verhaltensweisen.

6. Objektabfragesprache

Geode unterstützt auch Object Query Language (OQL), das leistungsfähiger als eine einfache Schlüsselsuche sein kann. Es ist ein bisschen wie SQL.

Verwenden wir für dieses Beispiel die zuvor erstellte Region "Beispielkunde".

Wenn wir noch ein paar Kunden hinzufügen:

Map data = new HashMap<>();
data.put(new CustomerKey(1), new Customer("Gheorge", "Manuc", 36));
data.put(new CustomerKey(2), new Customer("Allan", "McDowell", 43));
this.customerRegion.putAll(data);

Dann können wirQueryService verwenden, um Kunden zu finden, deren Vorname "Allan" ist:

QueryService queryService = this.cache.getQueryService();
String query =
  "select * from /example-customers c where c.firstName = 'Allan'";
SelectResults results =
  (SelectResults) queryService.newQuery(query).execute();
assertEquals(1, results.size());

7. Funktion

Eine der mächtigeren Vorstellungen von speicherinternen Datengittern ist die Idee, „die Berechnungen auf die Daten zu übertragen“.

Einfach ausgedrückt, da Geode reines Java ist,it’s easy for us to not only send data but also logic to perform on that data.

Dies könnte uns an die Idee von SQL-Erweiterungen wie PL-SQL oder Transact-SQL erinnern.

7.1. Eine Funktion definieren

Um eine Arbeitseinheit für Geode zu definieren, implementiert we dieFunction-Schnittstelle von Geode.

Stellen wir uns zum Beispiel vor, wir müssen alle Kundennamen in Großbuchstaben ändern.

Anstatt die Daten abzufragen und unsere Anwendung die Arbeit erledigen zu lassen, können wir einfachFunction implementieren:

public class UpperCaseNames implements Function {
    @Override
    public void execute(FunctionContext context) {
        RegionFunctionContext regionContext = (RegionFunctionContext) context;
        Region region = regionContext.getDataSet();

        for ( Map.Entry entry : region.entrySet() ) {
            Customer customer = entry.getValue();
            customer.setFirstName(customer.getFirstName().toUpperCase());
        }
        context.getResultSender().lastResult(true);
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}

Beachten Sie, dassgetId must einen eindeutigen Wert zurückgibt, sodass der Klassenname normalerweise eine gute Wahl ist.

DasFunctionContext enthält alle unsere Regionsdaten, sodass wir eine komplexere Abfrage daraus durchführen oder, wie wir es hier getan haben, es mutieren können.

UndFunction hat viel mehr Leistung als diese. Schauen Sie sich alsothe official manual an, insbesonderethe getResultSender method.

7.2. Bereitstellen der Funktion

Wir müssen Geode auf unsere Funktion aufmerksam machen, um sie ausführen zu können. Wie bei unseren benutzerdefinierten Datentypen verpacken wir das Glas.

Diesmal können wir jedoch nur den Befehldeployverwenden:

gfsh> deploy --jar=./lib/apache-geode-1.0-SNAPSHOT.jar

7.3. Funktion ausführen

Jetzt können wir dieFunction aus der Anwendung mit denFunctionService: ausführen

@Test
public void whenExecuteUppercaseNames_thenCustomerNamesAreUppercased() {
    Execution execution = FunctionService.onRegion(this.customerRegion);
    execution.execute(UpperCaseNames.class.getName());
    Customer customer = this.customerRegion.get(new CustomerKey(1));
    assertEquals("GHEORGE", customer.getFirstName());
}

8. Fazit

In diesem Artikel haben wir die Grundkonzepte desApache Geode-Ökosystems kennengelernt. Wir haben uns einfache Gets und Puts mit Standard- und benutzerdefinierten Typen, replizierten und partitionierten Regionen sowie OQL- und Funktionsunterstützung angesehen.

Und wie immer sind alle diese Proben inover on GitHub verfügbar.