Redis Store (Lettus) Adapter

Provide an adapter that allows Redis (External sites) to be used for the session store.

Using Redis as a session store provides the following advantages over choosing a DB store.

  • No need to prepare a table in advance to store session information
  • No need to make a batch to remove expired session information

Running in a minimal configuration

This section describes how to configure it, with the example of connecting to a single Redis instance running on port 6379 of the localhost .

Tip

If want to try it locally, can use Docker to build a Redis instance by executing the following command.

> docker run --name redis -d -p 6379:6379 redis:5.0.9

To stop, execute the command as follows.

> docker stop redis

Configuration

To start using the Redis store with minimal configuration, need to modify the component definitions and configuration values of the application.

Modify the component definition file

Describes how to modify the component definition file.

<!-- Omitted -->
<config-file file="nablarch/webui/redisstore-lettuce.config" />
<config-file file="common.properties" />
<config-file file="env.properties" />

<!-- Omitted -->
<import file="nablarch/webui/redisstore-lettuce.xml" />

First, load the following two configuration files, which are provided by the default configuration.

  • nablarch/webui/redisstore-lettuce.config
  • nablarch/webui/redisstore-lettuce.xml

redisstore-lettuce.config has already declared the default values for the placeholders used in redisstore-lettuce.xml .

If your application has a configuration file (such as env.properties ), redisstore-lettuce.config should be loaded before it. This way, can override the default placeholder values with the application’s configuration file, if necessary.

In addition, by using the methods described in Overriding Environment Dependencies with Overwrite environment dependent values using OS environment variables , it is possible to switch the destination Redis for each execution environment.

Tip

By default, it is configured to connect to a single Redis instance running on port 6379 of the localhost .

redisstore-lettuce.xml defines the components that are needed to use the Redis store.

If use redisstore-lettuce.xml , nablarch/webui/session-store.xml is no longer needed. If you are generating a project with web archetype , you are configured to use session-store.xml by default, so remove the import of session-store.xml and import redisstore-lettuce.xml instead Modify as.

<!-- Components that need to be initialized -->
<component name="initializer"
           class="nablarch.core.repository.initialization.BasicApplicationInitializer">
  <property name="initializeList">
    <list>
      <!-- Omitted -->
      <component-ref name="lettuceRedisClientProvider"/>
    </list>
  </property>
</component>

Next, add a component of the LettuceRedisClientProvider to the initializeList of the BasicApplicationInitializer .

The LettuceRedisClientProvider component is defined in redisstore-lettuce.xml under the name lettuceRedisClientProvider so that it can be configured using name references.

For an explanation of this configuration, see Initialize client classes .

<!-- Components that need to be discarded -->
<component name="disposer"
           class="nablarch.core.repository.disposal.BasicApplicationDisposer">
  <property name="disposableList">
    <list>
      <!-- Omitted -->
      <component-ref name="lettuceRedisClientProvider"/>
    </list>
  </property>
</component>

In addition, add a component of the LettuceRedisClientProvider to the BasicApplicationDisposer ‘s disposableList .

For an explanation of this configuration, see Handling the disposal of objects .

Modify the environment settings values

Describe how to modify the environment setting values.

# The default session store name
nablarch.sessionManager.defaultStoreName=redis

In the project’s configuration file, define a configuration item named nablarch.sessionManager.defaultStoreName and set the value to redis .

Tip

If you are creating a project with web archetype , nablarch.sessionManager.defaultStoreName is declared in src/main/resources/common.properties .

Now can use Redis running on port 6379 of the localhost as a session store.

Configure it for Redis composition

Running in a minimal configuration showed an example of connecting to a single Redis instance that starts locally.

However, when actually use Redis in production environment, you need to be able to connect to Redis with the following composition.

  • Master-Replica composition with Sentinel
  • Cluster Composition

This section describes how to change the configuration depending on the Redis composition of the destination.

Client classes prepared per composition

This adapter provides a dedicated client class (a class that implements LettuceRedisClient ) for each Redis composition to connect to.

LettuceSimpleRedisClient
Class used to connect directly to a single Redis instance.
LettuceMasterReplicaRedisClient
Class to use when connecting to a Redis instance of Master-Replica composition. Use this class to connect through Sentinel.
LettuceClusterRedisClient
Class used to connect to a Redis instance in a Cluster composition.

Need to configure the client classes to be used from among these according to the Redis composition used by the application.

Tip

These client class components are defined in redisstore-lettuce.xml and do not need to be defined by the user.

Configure the client class to be used

Which client class is used is configurable by the environment setting key nablarch.lettuce.clientType .

The relationship between the values and the adopted client class is shown in the table below.

Value Client class
simple LettuceSimpleRedisClient
masterReplica LettuceMasterReplicaRedisClient
cluster LettuceClusterRedisClient

Therefore, can connect to Redis in a Cluster composition by configuring application’s configuration file as follows.

nablarch.lettuce.clientType=cluster

Tip

The default value for nablarch.lettuce.clientType is configured to be simple in redisstore-lettuce.config .

Configure the connection URI

The information of Redis to connect to is specified by URI.

URI can be configured for each Redis composition with the following environment setting values. URI can be configured for each Redis composition with the following environment settings keys.

Composition Key The default value (configured in redisstore-lettuce.config)
Single nablarch.lettuce.simple.uri redis://localhost:6379
Master-Replica nablarch.lettuce.masterReplica.uri redis-sentinel://localhost:26379,localhost:26380,localhost:26381?sentinelMasterId=masterGroupName
Cluster nablarch.lettuce.cluster.uriList redis://localhost:6379,redis://localhost:6380,redis://localhost:6381

The Cluster configuration value is a comma-separated list of URIs to connect to each node. For more information on the format of individual URIs, see Lettuce documentation(URI syntax) (external site) .

Do more advanced configurations

Only the client class type and URI can be specified in the environment settings values. If want to do more advanced configuration, need to create a custom client class that inherits from each client class.

In each client class, there is a protected method to create a Lettuce instance. The protected methods provided in each client class are listed in the following table.

Client class Method Return value type
LettuceSimpleRedisClient createClient() RedisClient(external site)
createConnection(RedisClient) StatefulRedisConnection<byte[], byte[]>(external site)
LettuceMasterReplicaRedisClient createClient() RedisClient(external site)
createConnection(RedisClient) StatefulRedisMasterReplicaConnection<byte[], byte[]>(external site)
LettuceClusterRedisClient createClient() RedisClusterClient(external site)
createConnection(RedisClusterClient) StatefulRedisClusterConnection<byte[], byte[]>(external site)

By overriding these methods in a custom client class and implementing them to return an instance of Lettuce with your own settings, you can make any settings you want.

Can then replace components of the client class by defining components of the custom client class with the same names as the original components.

The component names of each client class are shown in the table below.

Client class Component name
LettuceSimpleRedisClient lettuceSimpleRedisClient
LettuceMasterReplicaRedisClient lettuceMasterReplicaRedisClient
LettuceClusterRedisClient lettuceClusterRedisClient

Examples: Enable monitoring of Cluster topology updates

To describe the implementation and configuration of a custom client class, with an example setting to enable monitoring of the Cluster topology updates.

First, create a custom client class (CustomClusterRedisClient) that inherits from the LettuceClusterRedisClient client class for cluster composition.

package com.nablarch.example.redisstore;

import io.lettuce.core.RedisURI;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import nablarch.integration.redisstore.lettuce.LettuceClusterRedisClient;

import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

public class CustomClusterRedisClient extends LettuceClusterRedisClient {

    @Override
    protected RedisClusterClient createClient() {
        List<RedisURI> redisUriList = uriList.stream().map(RedisURI::create).collect(Collectors.toList());
        RedisClusterClient client = RedisClusterClient.create(redisUriList);

        ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                .enableAllAdaptiveRefreshTriggers()
                .enablePeriodicRefresh(Duration.ofSeconds(10))
                .build();

        ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
                .topologyRefreshOptions(clusterTopologyRefreshOptions)
                .build();

        client.setOptions(clusterClientOptions);

        return client;
    }
}

To enable Lettuce to monitor cluster topology updates, you need to set ClusterTopologyRefreshOptions (external site) to RedisClusterClient (external site) with the necessary settings.

Therefore, implement the CustomClusterRedisClient by overriding createClient() , which creates a RedisClusterClient , to return an instance of the RedisClusterClient with the necessary settings.

Tip

For more information on settings of Lettuce, see Lettuce documentation(Cluster-specific options) (external site) .

Next, define this custom client class as the component.

<import file="nablarch/webui/redisstore-lettuce.xml" />

<component name="lettuceClusterRedisClient" class="com.nablarch.example.redisstore.CustomClusterRedisClient">
  <property name="uriList" ref="redisClusterUriListFactory" />
</component>

Since the original client class of CustomClusterRedisClient is LettuceClusterRedisClient , can override the component by defining it by the name lettuceClusterRedisClient .

The configuration of the uriList property is the same as in the original redisstore-lettuce.xml . If you create a class that extends another client class, the property settings should be the same as in redisstore-lettuce.xml .

Now it is possible to monitor the topology updates.

Mechanism for determining the client class to use

In the section Configure the client class to be used , we described how the client class to be used can be set using the Environment settings key nablarch.lettuce.clientType . In this section, will describe how the client class is determined and the details of the mechanism.

Which of the components of the three client classes is actually used is determined by LettuceRedisClientProvider .

LettuceRedisClientProvider is defined in redisstore-lettuce.xml as follows.

<component name="lettuceRedisClientProvider" class="nablarch.integration.redisstore.lettuce.LettuceRedisClientProvider">
    <property name="clientType" value="${nablarch.lettuce.clientType}" />
    <property name="clientList">
        <list>
            <component-ref name="lettuceSimpleRedisClient" />
            <component-ref name="lettuceMasterReplicaRedisClient" />
            <component-ref name="lettuceClusterRedisClient" />
        </list>
    </property>
</component>

This class has two properties: clientList and clientType .

The clientList is set to a list of candidate client class components. And in clientType , the identifier of the client class to be used is set.

Each client class has a method called getType() that returns its own identifier. LettuceRedisClientProvider compares the value set in the clientType property with the value in getType() returned by each component set in the clientList property. The component whose value matches is then determined to be the actual component to use.

LettuceRedisClientProvider implements ComponentFactory and the createObject() method is implemented to return components of the determined client class ( LettuceRedisClient ).

Initialize client classes

All three client classes provided by this adapter require initialization to establish a connection to Redis.

Each client class implements Initializable , and a connection to Redis is established by executing the initialize() method. Therefore, the component of the client class to be used must be configured for the initializeList property of BasicApplicationInitializer .

The actual configuration of the initializeList is achieved by using the function of LettuceRedisClientProvider component described in Mechanism for determining the client class to use as shown below.

<!-- Components that need to be initialized -->
<component name="initializer"
           class="nablarch.core.repository.initialization.BasicApplicationInitializer">
  <property name="initializeList">
    <list>
      <!-- Omitted -->
      <component-ref name="lettuceRedisClientProvider"/>
    </list>
  </property>
</component>

In this way, initialize the components of a determined client class without changing the description of the component definition.

Disposal of client classes

Each client class implements Disposable , and its connection to Redis is closed by executing the dispose() method. Therefore, by configuring the disposableList property of the BasicApplicationDisposer with the components of the client class to be used, the connection to Redis can be closed when the application exits.

<!-- Components that need to be disposal -->
<component name="disposer"
           class="nablarch.core.repository.disposal.BasicApplicationDisposer">
  <property name="disposableList">
    <list>
      <!-- Omitted  -->
      <component-ref name="lettuceRedisClientProvider"/>
    </list>
  </property>
</component>

Same way as the initializeList in the BasicApplicationInitializer , specify LettuceRedisClientProvider component in the disposableList property. This will perform the disposal process for the actual client class being used.

How to store session information

Session information stored in Redis is stored with the key nablarch.session.<session ID> .

The following shows the display of the keys stored in the redis-cli .

127.0.0.1:6379> keys *
1) "nablarch.session.8b00bce5-d19f-4f63-b1fe-d14ecca9a4f6"

The session information (the list of SessionEntry) is stored in binary format, encoded by default with JavaSerializeStateEncoder .

The encoder used can be changed by defining encoder component named serializeEncoder .

How to manage the expiration period

Redis provides a mechanism to set an expiration period for stored keys. Expired keys are automatically deleted.

This adapter uses the Redis expiration mechanism to manage the expiration of a session. Therefore, since expired session information is automatically deleted, there is no need to prepare a batch to delete the session information that remains as garbage.

The following shows the expiration period of the session information being checked with the pttl command (external site) .

127.0.0.1:6379> pttl "nablarch.session.8b00bce5-d19f-4f63-b1fe-d14ecca9a4f6"
(integer) 879774