Installation
To use a counter in your Infinispan cluster, first you have to add the maven dependency to your project. As you can see, it is simple as doing:<dependency> | |
<groupId>org.infinispan</groupId> | |
<artifactId>infinispan-clustered-counter</artifactId> | |
<version>9.1.0.Final</version> | |
</dependency> |
CounterManager
Each CounterManager is associated to a CacheManager. But, before showing how to use it, first we have some configuration to be done.There are two attributes that you can configure: The num-owner - that represents the number of copies of the counter's value in a cluster; and the reliability - that represents the behavior of the counters in case of partitions.
Below, is the configuration example with their default values.
XML:
<?xml version="1.0" encoding="UTF-8"?> | |
<infinispan> | |
<cache-container ...> | |
<!-- if you need to persist counter (i.e. the counter's value survive cluster restarts), global state needs to be configured --> | |
<global-state> | |
... | |
</global-state> | |
<!-- your caches configuration goes here --> | |
<counters xmlns="urn:infinispan:config:counters:9.1" num-owners="2" reliability="AVAILABLE"/> | |
</cache-container> | |
</infinispan> |
// Setup up a clustered cache manager | |
GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder(); | |
// Create the counter configuration manager builder | |
CounterManagerConfigurationBuilder counterBuilder = global.addModule(CounterManagerConfigurationBuilder.class); | |
counterBuilder.numOwner(2).reliability(Reliability.AVAILABLE); |
// Initialize the cache manager | |
DefaultCacheManager cacheManager = new DefaultCacheManager(/*your configuration*/); | |
// Retrieve the CounterManager from the CacheManager. Each CacheManager has it own CounterManager | |
CounterManager counterManager = EmbeddedCounterManagerFactory.asCounterManager(cacheManager); |
Counter
A counter is identified by its name. Also, it is initialized with an initial value (by default 0) and it can be persisted, if the value needs to survive a cluster restart.Strong Counters
The strong counter provides higher consistency. Its value is known during the update and its updates are applied atomically. This allows to set boundaries and provides conditional operation (as compare-and-set).Configuration
A strong counter can be configured in the configuration file or programatically. They can be also created dynamically at runtime. Below shows us how it can be done:XML:
<counters xmlns="urn:infinispan:config:counters:9.1" ...> | |
<!-- only the name is mandatory! --> | |
<strong-counter name="my-counter" initial-value="0" storage="VOLATILE"> | |
<!-- only set an upper-bound or lower-bound if you want to apply boundaries! --> | |
<lower-bound value="0"/> | |
<upper-bound value="10"/> | |
</strong-counter> | |
</counters> |
// Create the counter configuration builder | |
CounterManagerConfigurationBuilder counterBuilder = ... | |
// Bounded counter from 0 to 10. | |
// Only the name() is mandatory. | |
// Do not invoke the lowerBound() or upperBound() if you want an unbounded counter. | |
counterBuilder.addStrongCounter().name("my-counter").initialValue(0).storage(Storage.VOLATILE).lowerBound(0).upperBound(10); |
// Retrieve the CounterManager | |
CounterManager counterManager = ,..; | |
// Defines an unbounded counter. | |
// initialValue() and storage() are optional. | |
manager.defineCounter("my-counter", CounterConfiguration.builder(CounterType.UNBOUNDED_STRONG).initialValue(0).storage(Storage.VOLATILE)build()); | |
// Defines a bounded counter. | |
// Sets the boundaries with lowerBound() and/or upperBound(). If they are not set, Long.MIN_VALUE and Long.MAX_VALUE will be used. | |
// initialValue() and storage() are optional. | |
manager.defineCounter("my-bounded-counter", CounterConfiguration.builder(CounterType.BOUNDED_STRONG).initialValue(0).lowerBound(0).upperBound(10).storage(Storage.VOLATILE).build()); |
Use Case
The strong counter fits the following uses cases:- Global Id Generator
public long generateId() throws ExecutionException, InterruptedException { | |
return strongCounter.incrementAndGet().get(); | |
} |
- Rate Limiter
public void handleRequest() throws InterruptedException, ExecutionException { | |
try { | |
strongCounter.incrementAndGet().get(); | |
// you can proceed with the request | |
} catch (ExecutionException e) { | |
if (e.getCause() instanceof CounterOutOfBoundsException) { | |
// unable to handle request. Limit reached. | |
} else { | |
throw e; | |
} | |
} | |
} | |
// Invoked every X seconds | |
public void reset() { | |
strongCounter.reset(); | |
} |
- Simply count "stuff"
public long receiveApple() throws ExecutionException, InterruptedException { | |
return appleCounter.incrementAndGet().whenComplete((nApples, throwable) -> System.out.println("Now I have " + nApples + " apples!")).get(); | |
} | |
public long countApples() throws ExecutionException, InterruptedException { | |
return appleCounter.getValue().get(); | |
} |
Weak Counters
Configuration
As in strong counter, the weak counter can be configure its name and its initial value. In addition, a concurrency-level can be configure to set the number of concurrent updates that can be handled in parallel. Below shows us how to configure it:XML:
<counters xmlns="urn:infinispan:config:counters:9.1" ...> | |
<!-- only the name is mandatory! --> | |
<weak-counter name="my-counter" initial-value="5" storage="VOLATILE" concurrency-level="1024"/> | |
</counters> |
// Create CounterManagerConfigurationBuilder | |
CounterManagerConfigurationBuilder counterBuilder = ... | |
// Only name() is mandatory | |
counterBuilder.addWeakCounter().name("my-counter").storage(Storage.VOLATILE).concurrencyLevel(1024); |
// Retrieve CounterManager | |
CounterManager counterManager = ... | |
// initialValue(), storage() and concurrencyLevel() are optional. | |
counterManager.defineCounter("my-counter", CounterConfiguration.builder(CounterType.WEAK).initialValue(5).storage(Storage.VOLATILE).concurrencyLevel(1024).build()); |
Use Case
public void visitPage() { | |
visitorCounter.increment(); | |
} | |
public long getNumberOfVisitors() { | |
return visitorCounter.getValue(); | |
} |
For more information, take a look at the documentation. If you have any feedback, or would like to request some new features, or found some issue, let us know via the forum, issue tracker or the #infinispan channel on Freenode.