第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();