1. 概要

Apache Geode は、キャッシュとデータ計算をサポートする分散型メモリ内データグリッドです。

このチュートリアルでは、Geodeの主要な概念について説明し、Javaクライアントを使用していくつかのコードサンプルを実行します。

2. 設定

まず、Apache Geodeをダウンロードしてインストールし、gfsh環境を設定する必要があります。 これを行うには、Geodeの公式ガイドの指示に従うことができます。

次に、このチュートリアルでは、いくつかのファイルシステムアーティファクトを作成します。 したがって、一時ディレクトリを作成し、そこから起動することで、それらを分離できます。

2.1. インストールと構成

一時ディレクトリから、Locatorインスタンスを開始する必要があります。

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

ロケーターは、Geodeクラスターのさまざまなメンバー間の調整を担当します。これは、JMXを介してさらに管理できます。

次に、 Server インスタンスを起動して、1つ以上のデータRegionをホストします。

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

–server-port オプションを0に設定して、Geodeが使用可能なポートを選択できるようにします。 ただし、省略した場合、サーバーはデフォルトのポート40404を使用します。 サーバーは、長期間有効なプロセスとして実行され、データリージョンの管理を担当するクラスターの構成可能なメンバーです。

そして最後に、Regionが必要です。

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

リージョンは、最終的にデータを保存する場所です。

2.2. 検証

先に進む前に、すべてが機能していることを確認しましょう。

まず、サーバーロケーターがあるかどうかを確認しましょう。

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

次に、Regionがあります。

gfsh> describe region --name=baeldung
..........................................................
Name            : baeldung
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

また、ファイルシステムの「locator」および「server1」という一時ディレクトリの下にいくつかのディレクトリが必要です。

この出力により、次に進む準備ができていることがわかります。

3. Mavenの依存関係

Geodeを実行しているので、クライアントコードを見てみましょう。

JavaコードでGeodeを操作するには、 ApacheGeodeJavaクライアントライブラリをpomに追加する必要があります。

<dependency>
     <groupId>org.apache.geode</groupId>
     <artifactId>geode-core</artifactId>
     <version>1.6.0</version>
</dependency>

まず、いくつかのデータをいくつかのリージョンに保存して取得することから始めましょう。

4. シンプルな保管と検索

単一の値、値のバッチ、およびカスタムオブジェクトを格納する方法を示しましょう。

「baeldung」リージョンへのデータの保存を開始するには、ロケーターを使用してデータに接続しましょう。

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

4.1. 単一値の保存

これで、リージョン内のデータを簡単に保存および取得できます。

@Test
public void whenSendMessageToRegion_thenMessageSavedSuccessfully() {

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

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

4.2. 複数の値を一度に保存

ネットワーク遅延を削減しようとする場合など、一度に複数の値を保存することもできます。

@Test
public void whenPutMultipleValuesAtOnce_thenValuesSavedSuccessfully() {

    Supplier<Stream<String>> keys = () -> Stream.of("A", "B", "C", "D", "E");
    Map<String, String> 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. カスタムオブジェクトの保存

文字列は便利ですが、後でではなく早くカスタムオブジェクトを保存する必要があります。

次のキータイプを使用して保存したい顧客レコードがあると想像してください。

public class CustomerKey implements Serializable {
    private long id;
    private String country;
    
    // getters and setters
    // equals and hashcode
}

そして、次の値型:

public class Customer implements Serializable {
    private CustomerKey key;
    private String firstName;
    private String lastName;
    private Integer age;
    
    // getters and setters 
}

これらを保存できるようにするための追加の手順がいくつかあります。

初め、 それらはSerializableを実装する必要があります。 これは厳密な要件ではありませんが、 シリアル化可能、 Geodeはそれらをより堅牢に保存できます

次に、アプリケーションのクラスパスとGeodeServerのクラスパスに存在する必要があります。

それらをサーバーのクラスパスに取得するには、 mvn cleanpackageを使用してパッケージ化します。

次に、新しい startserverコマンドで結果のjarを参照できます。

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

ここでも、一時ディレクトリからこれらのコマンドを実行する必要があります。

最後に、「baeldung」リージョンの作成に使用したのと同じコマンドを使用して、Serverに「baeldung-customers」という名前の新しいRegionを作成しましょう。

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

コードでは、カスタムタイプを指定して、前と同じようにロケーターにアクセスします。

@Before
public void connect() {
    // ... connect through the locator
    this.customerRegion = this.cache.<CustomerKey, Customer> 
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("baeldung-customers");
}

そして、以前と同じように顧客を保存できます。

@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. 地域タイプ

ほとんどの環境では、読み取りと書き込みのスループット要件に応じて、リージョンの複数のコピーまたは複数のパーティションがあります。

これまで、メモリ内のレプリケートされた領域を使用してきました。 よく見てみましょう。

5.1. 複製された地域

名前が示すように、 レプリケートされたリージョンは、そのデータのコピーを複数のサーバーに保持します。 これをテストしてみましょう。

作業ディレクトリのgfshコンソールから、server2という名前のServerをもう1つクラスターに追加しましょう。

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

「baeldung」を作成したときは、 –type =REPLICATEを使用したことを思い出してください。 このため、Geodeはデータを新しいサーバーに自動的に複製します。

server1:を停止してこれを確認しましょう

gfsh> stop server --name=server1

次に、「baeldung」領域で簡単なクエリを実行してみましょう。

データが正常に複製された場合、結果が返されます。

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

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

つまり、レプリケーションが成功したようです。

リージョンにレプリカを追加すると、データの可用性が向上します。 また、複数のサーバーがクエリに応答できるため、読み取りスループットも向上します。

しかし、 両方がクラッシュした場合はどうなりますか? これらはメモリ内領域であるため、データは失われます。 このために、代わりに使用することができます –type = REPLICATE_PERSISTENT また、複製中にデータをディスクに保存します。

5.2. パーティション化された領域

データセットが大きい場合は、リージョンを個別のパーティションまたはバケットに分割するようにGeodeを構成することで、システムをより適切にスケーリングできます。

「baeldung-partitioned」という名前のパーティション化されたリージョンを1つ作成しましょう。

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

いくつかのデータを追加します。

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

そしてすぐに確認します:

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

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

次に、データがパーティション化されたことを検証するために、 server1 を再度停止して、再度クエリを実行します。

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

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

そのサーバーにはデータのパーティションが1つしかないため、今回は一部のデータエントリしか取得できませんでした。したがって、 server1 がドロップすると、そのデータは失われました。

しかし、パーティショニングと冗長性の両方が必要な場合はどうでしょうか。 Geodeもサポートしています他の多くのタイプ 。 次の3つが便利です。

  • PARTITION_REDUNDANTパーティションおよびは、クラスターのさまざまなメンバー間でデータを複製します
  • PARTITION_PERSISTENT は、 PARTITION のようにデータをパーティション化しますが、ディスクと
  • PARTITION_REDUNDANT_PERSISTENT は、3つの動作すべてを提供します。

6. オブジェクトクエリ言語

Geodeは、オブジェクトクエリ言語(OQL)もサポートしています。これは、単純なキールックアップよりも強力です。 SQLに少し似ています。

この例では、前に作成した「baeldung-customer」リージョンを使用してみましょう。

さらに2、3の顧客を追加すると、次のようになります。

Map<CustomerKey, Customer> 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);

次に、 QueryService を使用して、名が「Allan」である顧客を検索できます。

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

7. 関数

インメモリデータグリッドのより強力な概念の1つは、「データに対して計算を行う」という考え方です。

簡単に言えば、Geodeは純粋なJavaであるため、データを送信するだけでなく、そのデータに対して実行するロジックも簡単に実行できます。

これは、PL-SQLやTransact-SQLなどのSQL拡張機能のアイデアを思い出させるかもしれません。

7.1. 関数の定義

Geodeが実行する作業単位を定義するために、Geodeの関数インターフェースを実装します。

たとえば、すべての顧客の名前を大文字に変更する必要があるとします。

データをクエリしてアプリケーションに作業を行わせる代わりに、Functionを実装するだけです。

public class UpperCaseNames implements Function<Boolean> {
    @Override
    public void execute(FunctionContext<Boolean> context) {
        RegionFunctionContext regionContext = (RegionFunctionContext) context;
        Region<CustomerKey, Customer> region = regionContext.getDataSet();

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

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

getId は一意の値を返す必要があるため、通常はクラス名を選択することをお勧めします。

FunctionContext にはすべてのリージョンデータが含まれているため、そこからより高度なクエリを実行したり、ここで行ったように変更したりできます。

また、関数はこれよりもはるかに強力なので、公式マニュアル、特にgetResultSenderメソッドを確認してください。

7.2. 機能の展開

関数を実行できるようにするには、Geodeに関数を認識させる必要があります。 カスタムデータ型で行ったように、jarをパッケージ化します。

ただし、今回はdeployコマンドを使用できます。

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

7.3. 機能の実行

これで、 FunctionService:を使用して、アプリケーションからFunctionを実行できます。

@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. 結論

この記事では、 ApacheGeodeエコシステムの基本的な概念を学びました。 標準タイプとカスタムタイプ、レプリケートされたリージョンとパーティション化されたリージョン、およびoqlと関数のサポートを使用した単純なgetとputについて説明しました。

そしていつものように、これらのサンプルはすべてGitHub利用できます。