第14章 クラスター化カウンター
クラスター化カウンターは、Red Hat JBoss Data Grid クラスターのノードすべてで分散および共有されます。クラスター化カウンターを使用するとオブジェクトの数を記録することができます。
クラスター化カウンターは名前で識別され、値 (デフォルトは 0) で初期化されます。クラスター化カウンターを永続化して、クラスターの再起動後に値を保持することもできます。
クラスター化カウンターには次の 2 種類があります。
-
Strongはカウンターの値を単一のキーに保存して一貫性を保ちます。カウンターの更新中でも値は認識できます。カウンター値の更新は、キーロック下で実行されます。しかし、カウンターの現在値を読み取るのに必要なロックはありません。Strong カウンターでは、カウンター値をバインドでき、compareAndSetやcompareAndSwapなどのアトミック操作を提供します。 -
Weakはカウンター値を複数のキーに格納します。各キーはカウンター値の部分的な状態を保存します。キーは同時に更新することが可能です。カウンターの更新中、値は認識できません。カウンター値を取得しても、常に最新の値を返すとは限りません。
Strong および Weak クラスター化カウンターの両方は、カウンター値の更新をサポートし、カウンターの現在の値を返し、カウンター値の更新時にイベントを提供します。
14.1. Counter API リンクのコピーリンクがクリップボードにコピーされました!
counter API は以下で構成されます。
-
EmbeddedCounterManagerFactoryは埋め込みのキャッシュマネージャーからカウンターマネージャーを初期化します。 -
RemoteCounterManagerFactoryはリモートキャッシュマネージャーからカウンターマネージャーを初期化します。 -
CounterManagerは、カウンターの作成、定義および返却を行うメソッドを提供します。 -
StrongCounterは strong カウンターを実装します。このインターフェースはカウンターのアトミックアップデートを提供します。操作はすべて非同期で実行され、完了ロジックにCompletableFutureクラスを使用します。 -
SyncStrongCounterは同期の strong カウンターを実装します。 -
WeakCounterは weak カウンターを実装します。すべての操作は非同期に実行され、完了ロジックにCompletableFutureクラスが使用されます。 -
SyncWeakCounterは同期の weak カウンターを実装します。 -
CounterListenerは、strong カウンターへの変更をリッスンします。 -
CounterEventは strong カウンターへの変更が発生したときにイベントを返します。 -
HandleはCounterListenerインターフェースを拡張します。
14.2. Maven 依存関係の追加 リンクのコピーリンクがクリップボードにコピーされました!
クラスター化カウンターの使用を開始するには、以下の依存関係を pom.xml に追加します。
pom.xml
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-clustered-counter</artifactId>
<version>...</version> <!-- 7.2.0 or later -->
</dependency>
14.3. CounterManager インターフェースの読み出し リンクのコピーリンクがクリップボードにコピーされました!
Red Hat JBoss Data Grid 埋め込みモードでクラスター化カウンターを使用するには、以下を行います。
// Create or obtain an EmbeddedCacheManager.
EmbeddedCacheManager manager = ...;
// Retrieve the CounterManager interface.
CounterManager counterManager = EmbeddedCounterManagerFactory.asCounterManager(manager);
Red Hat JBoss Data Grid リモートサーバーと対話する Hot Rod クライアントとクラスター化カウンターを使用するには、以下を行います。
// Create or obtain a RemoteCacheManager.
RemoteCacheManager manager = ...;
// Retrieve the CounterManager interface.
CounterManager counterManager = RemoteCounterManagerFactory.asCounterManager(manager);
14.4. クラスター化カウンターの使用 リンクのコピーリンクがクリップボードにコピーされました!
クラスター化カウンターの定義および設定は、cache-container XML 設定またはプログラムで行います。
14.4.1. クラスター化カウンターの XML 設定 リンクのコピーリンクがクリップボードにコピーされました!
以下の XML スニペットは、クラスター化カウンターの設定例を表しています。
<?xml version="1.0" encoding="UTF-8"?>
<infinispan>
<cache-container>
<!-- cache container configuration goes here -->
<!-- cache configuration goes here -->
<counters>
<strong-counter name="counter-1" initial-value="1">
<upper-bound value="10"/>
</strong-counter>
<strong-counter name="counter-2" initial-value="2"/>
<weak-counter name="counter-3" initial-value="3"/>
</counters>
</cache-container>
</infinispan>
14.4.1.1. XML 定義 リンクのコピーリンクがクリップボードにコピーされました!
counters 要素はクラスターのカウンターを設定し、以下の属性があります。
-
num-ownersは、クラスター全体で保存する各カウンターのコピー数を設定します。値が小さいと更新操作が速くなりますが、サポートするサーバーのクラッシュ数が少なくなります。値は正の整数である必要があります。デフォルトは2です。 reliabilityはネットワークパーティションでのカウンター更新動作を設定し、以下の値を取ります。-
AVAILABLE: すべてのパーティションがカウンターの値を読み取りおよび更新できます。これはデフォルト値です。 -
CONSISTENT: プライマリーパーティションがカウンターの値を読み取りおよび更新できます。残りのパーティションはカウンターの値の読み取りのみが可能です。
-
strong-counter 要素は、strong クラスター化カウンターを作成および定義します。weak-counter 要素は weak クラスター化カウンターを作成および定義します。以下は、両方の要素に共通する属性です。
-
initial-valueはカウンターの初期値を設定します。デフォルトの値は0です。 storageはカウンター値の保存方法を設定します。この属性は、クラスターのシャットダウン後および再起動後にカウンター値が保存されるかどうかを決定します。この属性は以下の値を取ります。-
VOLATILE: カウンターの値をメモリーに保存します。カウンターの値はクラスターのシャットダウン時に破棄されます。これがデフォルトの値になります。 -
PERSISTENT: カウンターの値はプライベートなローカル永続ストアに保存されます。カウンターの値は、クラスターのシャットダウン時および起動時に保存されます。
-
strong-counter 要素に固有する属性は次のとおりです。
-
lower-boundは strong カウンターの下限を設定します。デフォルトの値はLong.MIN_VALUEです。 -
upper-boundは strong カウンターの上限を設定します。デフォルトの値はLong.MAX_VALUEです。
initial-value 属性の値は、lower-bound の値と upper-bound の値の間である必要があります。strong カウンターの上限と下限を指定しないと、カウンターはバインドされません。
weak-counter 要素に固有する属性は次のとおりです。
-
concurrency-levelはカウンターの値の最大同時更新数を設定します。値は正の整数である必要があります。デフォルトの値は16です。
14.4.2. クラスター化カウンターのランタイム設定 リンクのコピーリンクがクリップボードにコピーされました!
以下の例のように、EmbeddedCacheManager の初期化後、クラスター化カウンターを起動時にオンデマンドで設定することができます。
CounterManager manager = ...;
// Create three counters.
// The first counter is a strong counter bounded to 10.
manager.defineCounter("counter-1", CounterConfiguration.builder(CounterType.BOUNDED_STRONG).initialValue(1).upperBound(10).build());
// The second counter is an unbounded strong counter.
manager.defineCounter("counter-2", CounterConfiguration.builder(CounterType.UNBOUNDED_STRONG).initialValue(2).build());
// The third counter is a weak counter.
manager.defineCounter("counter-3", CounterConfiguration.builder(CounterType.WEAK).initialValue(3).build());
カウンターが正常に定義された場合、defineCounter() メソッドは true を返し、そうでない場合は false を返します。カウンターの設定が有効でない場合は CounterConfigurationException 例外が発生します。
以下の例のように isDefined() メソッドを使用して、カウンターがすでに定義されているかどうかを判断します。
CounterManager manager = ...
if (!manager.isDefined("someCounter")) {
manager.define("someCounter", ...);
}
14.4.3. クラスター化カウンターのプログラムによる設定 リンクのコピーリンクがクリップボードにコピーされました!
以下のコードサンプルは、GlobalConfigurationBuilder を使用してクラスター化カウンターをプログラミングで設定する方法を表しています。
// Set up a clustered cache manager.
GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder();
// Create a counter configuration builder.
CounterManagerConfigurationBuilder builder = global.addModule(CounterManagerConfigurationBuilder.class);
// Create three counters.
// The first counter is a strong counter bounded to 10.
builder.addStrongCounter().name("counter-1").upperBound(10).initialValue(1);
// The second counter is an unbounded strong counter.
builder.addStrongCounter().name("counter-2").initialValue(2);
// The third counter is a weak counter.
builder.addWeakCounter().name("counter-3").initialValue(3);
// Initialize a new default cache manager.
DefaultCacheManager cacheManager = new DefaultCacheManager(global.build());
14.4.3.1. クラスター化カウンターの使用 リンクのコピーリンクがクリップボードにコピーされました!
以下のコードサンプルは、プログラムで作成および定義したクラスター化カウンターを使用する方法を表しています。
// Retrieve the CounterManager interface from the cache manager.
CounterManager counterManager = EmbeddedCounterManagerFactory.asCounterManager(cacheManager);
// Strong counters provide greater consistency than weak counters.
// The value of a strong counter is known during an increment or decrement operation.
// The value of a strong counter can also be bounded in cases where a limit is required.
StrongCounter counter1 = counterManager.getStrongCounter("counter-1");
// All methods are asynchronous and return CompletableFuture objects so that you can perform other operations while the counter value is computed.
counter1.getValue().thenAccept(value -> System.out.println("Counter-1 initial value is " + value)).get();
// Attempt to add a value that exceeds the upper-bound value.
counter1.addAndGet(10).handle((value, throwable) -> {
// Value is null since the counter is bounded to a maximum of 10.
System.out.println("Counter-1 Exception is " + throwable.getMessage());
return 0;
}).get();
// Check the counter value. The value should be 10.
counter1.getValue().thenAccept(value -> System.out.println("Counter-1 value is " + value)).get();
//Decrement the counter value. The new value should be 9.
counter1.decrementAndGet().handle((value, throwable) -> {
// No exception is thrown.
System.out.println("Counter-1 new value is " + value);
return value;
}).get();
// The second counter, counter2, is a strong counter that is unbounded. It never throws the CounterOutOfBoundsException.
StrongCounter counter2 = counterManager.getStrongCounter("counter-2");
// All counters allow a listener to be registered.
// The handle interface can remove the listener.
counter2.addListener(event -> System.out
.println("Counter-2 event: oldValue=" + event.getOldValue() + " newValue=" + event.getNewValue()));
// Adding MAX_VALUE does not throw an exception.
// No increments take effect if the value exceeds the MAX_VALUE.
counter2.addAndGet(Long.MAX_VALUE).thenAccept(aLong -> System.out.println("Counter-2 value is " + aLong)).get();
// Conditional operations are allowed in strong counters.
counter2.compareAndSet(Long.MAX_VALUE, 0)
.thenAccept(aBoolean -> System.out.println("Counter-2 CAS result is " + aBoolean)).get();
counter2.getValue().thenAccept(value -> System.out.println("Counter-2 value is " + value)).get();
// Reset the value of the second counter to its initial value.
counter2.reset().get();
counter2.getValue().thenAccept(value -> System.out.println("Counter-2 initial value is " + value)).get();
// Retrieve the third counter, counter3.
WeakCounter counter3 = counterManager.getWeakCounter("counter-3");
// The value of weak counters is not available during update operations. As a result these counters can increment faster than strong counters.
// The counter value is computed lazily and stored locally.
counter3.add(5).thenAccept(aVoid -> System.out.println("Adding 5 to counter-3 completed!")).get();
// Check the counter value.
System.out.println("Counter-3 value is " + counter3.getValue());
// Stop the cache manager and release all resources.
cacheManager.stop();