hazelcast - Skullabs/kikaha GitHub Wiki
Hazelcast is an open source in-memory data grid. It was designed to store data in-memory but in a distributed fashion, similarly to Memcached and Redis. The kikaha-hazelcast
module integrated the most used structures that Hazelcast offers. This guide will cover these features and how do configure them. For a complete set of configurations and features that Hazelcast offers, please refer to the Hazelcast documentation.
To enable this integration, just include the Hazelcast module on your classpath.
<dependency>
<groupId>io.skullabs.kikaha</groupId>
<artifactId>kikaha-cloud-hazelcast</artifactId>
</dependency>
There is a few ways to configure Hazelcast, but we recommend you to use one of bellow:
- Hazelcast's XML configuration file - which is broadly referenced on its documentation page
- Hazelcast's programmatic API
Define where is the hazelcast.xml
file you want to use. As recommended, the bellow sample configuration suggests you to place the hazelcast.xml
file at the <resources_dir>/conf
directory.
server:
hazelcast:
config: "conf/hazelcast.xml"
Now, you can configure Hazelcast the same way is documented at Hazelcast's Manual.
Every time you start a Kikaha application you can be notified every time a new Hazelcast Instance will be used. This would allow you to configure Hazelcast globally, defining every single detail you need to have it ready for your use. The bellow code shows how one can be notified from Hazelcast Instances' configurations and configure a few elements before the instance be started.
@Slf4j
@Singleton
public class MyCacheConfiguration implements HazelcastConfigurationListener {
public final static String MAP_NAME = "my-cache";
@Override
public void onConfigurationLoaded(Config hzConfig) throws IOException {
MapConfig cacheMap = hzConfig.getMapConfigs().computeIfAbsent( MAP, k -> new MapConfig().setName(k) );
configureIMapWithDefaults( cacheMap );
}
void configureIMapWithDefaults( MapConfig mapConfig ){
log.info( "Configuring cache '"+mapConfig.getName()+"'..." );
mapConfig.setEvictionPolicy( EvictionPolicy.LFU )
.setAsyncBackupCount( 1 )
.setReadBackupData( true )
.setMaxSizeConfig( new MaxSizeConfig(3000, MaxSizeConfig.MaxSizePolicy.PER_NODE ) );
}
}
As you probably noticed, the above code implements the kikaha.hazelcast.HazelcastConfigurationListener
. Every implementation of this interface will be notified with the com.hazelcast.config.Config
when a new Hazelcast Instance is about to be created.
Hazelcast offers distributed implementations of Java interfaces. You can find a Java interface list of all available structure Hazelcast offers here.
Despite being very useful, use Hazelcast could be very labor and verbose. Kikaha's Hazelcast module is a tiny layer that integrates most of these distributed structures with the CDI module, allowing developers to inject Hazelcast structures anywhere. Bellow, there is a class with all Hazelcast structures developers are allowed to inject on its classes.
Note that Hazelcast module uses the
javax.inject.Named
annotation to identify the Distributed Structure is about to be instantiated. It is a mandatory annotation.
import javax.inject.*;
import java.util.*;
import java.concurrency.atomic.*;
import com.hazelcast.core.*;
@Singleton
public class SampleService {
@Inject
@Named( "atomic-booleans" )
IMap<Long, AtomicBoolean> map;
@Inject
@Named( "atomic-booleans" )
MultiMap<Long, AtomicBoolean> multimap;
@Inject
@Named( "atomic-booleans" )
IQueue<AtomicBoolean> queue;
@Inject
@Named( "atomic-booleans" )
ISet<AtomicBoolean> set;
@Inject
@Named( "atomic-booleans" )
IList<AtomicBoolean> list;
@Inject
@Named( "atomic-booleans" )
ITopic<AtomicBoolean> topic;
@Inject
@Named( "atomic-booleans" )
ReplicatedMap<Long,AtomicBoolean> replicatedMap;
@Inject
@Named( "executor-service" )
IExecutorService executorService;
@Inject
@Named( "ilock" )
ILock lock;
@Inject
@Named( "id-generator" )
IdGenerator idGenerator;
}
It is possible to be notified when Kikaha creates an instance of any Hazelcast's Distributes Data Strucutures (DDS). It helps to fill the gap when Hazelcast doesn't provide a data loader ou data store for a specific DDS.
Every time a distributable strucuture is created in the JVM, Kikaha searches for kikaha.hazelcast.HazelcastProducedDataListener
implementations. Bellow is illustrated how is ossible to define the initial ID for a Hazelcast IdGenerator
.
Note that, in order be reachable by the Kikaha's Hazelcast module at the start up time, you have to make all
kikaha.hazelcast.HazelcastProducedDataListener
implementations injectable.
import kikaha.hazelcast.HazelcastProducedDataListener;
import com.hazelcast.core.*;
import javax.inject.*;
@Singleton
public class IdGeneratorLoader implements HazelcastProducedDataListener<IdGenerator> {
// Inject your dao or JDBC connection here to retrieve data from DB
@Inject IdGeneratorDao dao;
@Override
public void dataProduced( IdGenerator data ){
long lastId = dao.loadLastIdFromDatabase( data.getName() );
data.init( lastId );
}
}
As described at the Authentication and authorization topic, Kikaha will store all logged in user's information into a kikaha.core.security.SessionStore
implementation. Kikaha's Hazelcast module implements a SessionStore that store any Session (and its data) into a com.hazelcast.core.IMap
called session-cache
. Developers are encouraged to configure the session-cache's eviction policy at the hazelcast.xml
file, or programmatically, in order to avoid OutOfMemoryException
.
Basically, to setup Hazelcast as your Session Manager, once you had added kikaha-hazelcast
as a dependency, you just need to configure the kikaha.hazelcast.HazelcastSessionStore
as your session store on your application.yml
file.
# application.yml
server:
# Authentication configuration
auth:
# Using Hazelcast to store sessions from UI
session-store: kikaha.hazelcast.HazelcastSessionStore
Bellow a sample hazelcast.xml
file, that evicts 25% of all stored session based on LRU eviction police. For more details about eviction policy, please consider read the Hazelcast's Manual entry about this topic.
<hazelcast
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<map name="default">
<time-to-live-seconds>0</time-to-live-seconds>
<max-idle-seconds>0</max-idle-seconds>
<eviction-policy>LRU</eviction-policy>
<max-size policy="PER_NODE">5000</max-size>
<eviction-percentage>25</eviction-percentage>
<min-eviction-check-millis>100</min-eviction-check-millis>
</map>
</hazelcast>