

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Lettuce クライアント設定 (Valkey および Redis OSS)
<a name="BestPractices.Clients-lettuce"></a>

このセクションでは、推奨される Java と Lettuce の設定オプションと、それらを ElastiCache クラスターに適用する方法について説明します。

このセクションの推奨事項は、Lettuce バージョン 6.2.2 でテスト済みです。

**Topics**
+ [例: TLS が有効な場合のクラスターモードの Lettuce の設定](BestPractices.Clients-lettuce-cme.md)
+ [例: TLS が有効でクラスターモードが無効な場合の Lettuce の設定](BestPractices.Clients-lettuce-cmd.md)

**Java DNS キャッシュ TTL**

Java 仮想マシン (JVM) は DNS 名参照をキャッシュします。JVM がホスト名を IP アドレスに変換するとき、*time-to-live* (TTL) と呼ばれる指定期間 IP アドレスをキャッシュします。

TTL 値の選択は、レイテンシーおよび変化に対する応答性と間のトレードオフです。TTL を短くすると、DNS リゾルバーはクラスターの DNS の更新をより早く認識します。これにより、クラスターで実行される置換やその他のワークフローにアプリケーションがより迅速に応答できるようになります。ただし、TTL が低すぎると、クエリの量が増え、それによってアプリケーションのレイテンシーが増加する可能性があります。絶対的に正しい TTL 値は存在しませんが、TTL 値を設定するときは、変更が有効になるまで待つことができる時間の長さについて検討する必要があります。

ElastiCache ノードは、変更される可能性がある DNS 名を使用するため、5～10 秒の低い TTL 値で JVM を設定することをお勧めします。これにより、ノードの IP アドレスが変更されたときに、アプリケーションは DNS エントリに対して再度クエリを実行することで、リソースの新しい IP アドレスを取得し、使用できるようになります。

一部の Java 設定では JVM のデフォルトの TTL が設定されるため、JVM が再起動されるまで、DNS エントリが更新されることはありません。

JVM TTL を設定する方法の詳細については、「[JVM TTL を設定する方法](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-jvm-ttl.html#how-to-set-the-jvm-ttl)」を参照してください。

**Lettuce のバージョン**

Lettuce のバージョン 6.2.2 以降の使用をお勧めします。

**エンドポイント**

クラスターモードが有効なクラスターを使用している場合は、`redisUri` をクラスター設定エンドポイントに設定します。この URI の DNS ルックアップは、クラスターで使用可能なすべてのノードのリストを返し、クラスターの初期化中にそれらのノードの 1 つにランダムに解決されます。トポロジの更新の仕組みの詳細については、このトピックの後半にある *dynamicRefreshSources* を参照してください。

**SocketOption**

[KeepAlive](https://lettuce.io/core/release/api/io/lettuce/core/SocketOptions.KeepAliveOptions.html) を有効にします。このオプションを有効にすると、コマンドのランタイムに失敗した接続を処理する必要が減ります。

[接続タイムアウトは](https://lettuce.io/core/release/api/io/lettuce/core/SocketOptions.Builder.html#connectTimeout-java.time.Duration-)、アプリケーションの要件とワークロードに基づいて設定してください。詳細については、このトピックで後述する「タイムアウト」のセクションを参照してください。

**ClusterClientOption:クラスターモードが有効なクライアントオプション**

接続が失われたときは [AutoReconnect](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterClientOptions.Builder.html#autoReconnect-boolean-) を有効にします。

[CommandTimeout](https://lettuce.io/core/release/api/io/lettuPrce/core/RedisURI.html#getTimeout--) を設定します。詳細については、このトピックで後述する「タイムアウト」のセクションを参照してください。

[NodeFilter](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterClientOptions.Builder.html#nodeFilter-java.util.function.Predicate-) を設定すると、障害が発生したノードをトポロジから除外できます。Lettuce は、「クラスターノード」出力にあるすべてのノード (PFAIL/FAIL ステータスのノードを含む) をクライアントの「パーティション」 (シャードとも呼ばれます) に保存します。クラスタートポロジを作成するプロセスで、すべてのパーティションノードに接続を試みます。障害が発生したノードを追加する Lettuce の動作は、何らかの理由でノードが交換されるときに接続エラー (または警告) を引き起こす可能性があります。

例えば、フェイルオーバーが完了してクラスターが回復プロセスを開始した後、clusterTopology が更新されている間、ダウンしているノードがトポロジから完全に削除されるまで、クラスターバスノードマップにそれが FAIL ノードとして短期間リストされます。この間、Lettuce クライアントはそのノードを正常なノードと見なし、継続的に接続します。そのため、再試行を使い果たすとエラーが発生します。

例えば、次のようになります。

```
final ClusterClientOptions clusterClientOptions = 
    ClusterClientOptions.builder()
    ... // other options
    .nodeFilter(it -> 
        ! (it.is(RedisClusterNode.NodeFlag.FAIL) 
        || it.is(RedisClusterNode.NodeFlag.EVENTUAL_FAIL) 
        || it.is(RedisClusterNode.NodeFlag.HANDSHAKE)
        || it.is(RedisClusterNode.NodeFlag.NOADDR)))
    .validateClusterNodeMembership(false)
    .build();
redisClusterClient.setOptions(clusterClientOptions);
```

**注記**  
ノードフィルタリングは、DynamicRefreshSources を true に設定して使用するのが最適です。そうしないと、1 つの問題のあるシードノードからトポロジビューを取得すると、一部のシャードのプライマリノードに障害が発生していると見なされ、このプライマリノードは除外され、スロットがカバーされなくなります。(DynamicRefreshSources が true の場合に) 複数のシードノードが存在すると、この問題が発生する可能性が低くなります。これは、新しく昇格したプライマリとのフェイルオーバー後に、少なくとも一部のシードノードでトポロジビューを更新する必要があるためです。

**ClusterTopologyRefreshOptions: クラスターモード対応クライアントのクラスタートポロジ更新を制御するオプション**

**注記**  
クラスターモードが無効なクラスターは、クラスター検出コマンドをサポートしていないため、すべてのクライアントの動的トポロジー検出機能と互換性があるわけではありません。  
ElastiCache で無効になっているクラスターモードは Lettuce の `MasterSlaveTopologyRefresh` と互換性がありません。代わりに、クラスタモードが無効になっている場合は、`StaticMasterReplicaTopologyProvider` を設定し、クラスターの読み取りと書き込みのエンドポイントを提供します。  
クラスターモードが無効なクラスターとの接続の詳細については、「[Valkey または Redis OSS (クラスターモードが無効) クラスターのエンドポイントを検索する (コンソール)](Endpoints.md#Endpoints.Find.Redis)」を参照してください。  
Lettuce の動的トポロジー検出機能を使いたい場合は、既存のクラスターと同じシャード構成でクラスターモードが有効なクラスターを作成できます。ただし、クラスターモードが有効なクラスターでは、高速フェールオーバーをサポートするために、少なくとも 3 つのシャードと 1 つのレプリカを構成することをお勧めします。

[enablePeriodicRefresh](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#enablePeriodicRefresh-java.time.Duration-) を有効にします。これにより、クラスタートポロジを定期的に更新できるため、クライアントは refreshPeriod の間隔 (デフォルト:60 秒) でクラスタートポロジを更新できます。無効にすると、クライアントはクラスターに対してコマンドの実行を試みたときにエラーが発生した場合にのみ、クラスタートポロジを更新します。

このオプションを有効にすると、このジョブをバックグラウンドタスクに追加することで、クラスタートポロジの更新に伴うレイテンシを減らすことができます。トポロジの更新はバックグラウンドジョブで実行されますが、多数のノードがあるクラスターでは多少遅くなる可能性があります。これは、すべてのノードが最新のクラスタービューを取得するためにそれらのビューに対してクエリが実行されているためです。大規模なクラスターを実行する場合は、この時間を長くすることをお勧めします。

[enableAllAdaptiveRefreshTriggers](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#enableAllAdaptiveRefreshTriggers--) を有効にします。これにより、MOVED\_REDIRECT、ASK\_REDIRECT、PERSISTENT\_RECONNECTS、UNCOVERED\_SLOT、UNKNOWN\_NODE のすべての[トリガー](https://lettuce.io/core/6.1.6.RELEASE/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.RefreshTrigger.html)を使用する適応型トポロジ更新が可能になります。適応型更新トリガーは、Valkey または Redis OSS クラスター操作中に発生したイベントに基づいてトポロジビューの更新を開始します。このオプションを有効にすると、前述のトリガーのいずれかが発生すると、トポロジがすぐに更新されます。適応型更新トリガーは、イベントが大規模で発生する可能性があるため (更新間のデフォルトタイムアウトは 30)、タイムアウトを使用してレート制限されます。

[closeStaleConnections](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#closeStaleConnections-boolean-) を有効にします。これにより、クラスタートポロジを更新するときに、古い接続を閉じることができます。[ClusterTopologyRefreshOptions.isPeriodicRefreshEnabled()](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.html#isPeriodicRefreshEnabled--) が true の場合にのみ有効になります。有効にすると、クライアントは古い接続を閉じて新しい接続をバックグラウンドで作成できます。これにより、コマンドのランタイムに失敗した接続を処理する必要が減ります。

[dynamicRefreshSources](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#dynamicRefreshSources-boolean-) を有効にします。小さなクラスターでは dynamicRefreshSources を有効にし、大きなクラスターでは無効にすることをお勧めします。 dynamicRefreshSources を使用すると、提供されたシードノード (クラスター設定エンドポイントなど) からクラスターノードを検出できます。検出されたすべてのノードを、クラスタートポロジを更新するためのソースとして使用します。

動的更新を使用すると、検出されたすべてのノードにクラスタートポロジを照会し、最も正確なクラスタービューを選択しようと試みます。false に設定すると、最初のシードノードのみがトポロジ検出のソースとして使用され、クライアント数は最初のシードノードについてのみ取得されます。無効になっている場合、クラスター設定エンドポイントが障害の発生したノードに解決されたとき、クラスタービューを更新しようとすると失敗し、例外が発生します。このシナリオは、障害が発生したノードのエントリがクラスター設定エンドポイントから削除されるまでに時間がかかるときに発生する可能性があります。そのため、設定エンドポイントは、障害が発生したノードに短期間ランダムに解決できます。

ただし、有効にすると、クラスタービューから受信したすべてのクラスターノードを使用して、現在のビューについてクエリを実行します。障害が発生したノードをそのビューから除外するので、トポロジ更新は成功します。ただし、dynamicRefreshSources が true の場合、Lettuce はすべてのノードにクエリを実行してクラスタービューを取得し、結果を比較します。そのため、多数のノードを持つクラスターではコストがかかる可能性があります。多数のノードがあるクラスターでは、この機能をオフにすることをお勧めします。

```
final ClusterTopologyRefreshOptions topologyOptions = 
    ClusterTopologyRefreshOptions.builder()
    .enableAllAdaptiveRefreshTriggers()
    .enablePeriodicRefresh()
    .dynamicRefreshSources(true)
    .build();
```

**ClientResources**

[DnsResolver](https://lettuce.io/core/release/api/io/lettuce/core/resource/DefaultClientResources.Builder.html#dnsResolver-io.lettuce.core.resource.DnsResolver-) を [DirContextDnsResolver](https://lettuce.io/core/release/api/io/lettuce/core/resource/DirContextDnsResolver.html) で設定します。DNS リゾルバーは Java の com.sun.jndi.dns.DnsContextFactory をベースにしています。

[reconnectDelay](https://lettuce.io/core/release/api/io/lettuce/core/resource/DefaultClientResources.Builder.html#reconnectDelay-io.lettuce.core.resource.Delay-) をエクスポネンシャルバックオフとフルジッターで設定します。Lettuce には、エクスポネンシャルバックオフ戦略に基づく再試行メカニズムが組み込まれています。詳細については、 AWS アーキテクチャブログの[「エクスポネンシャルバックオフとジッター](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter)」を参照してください。再試行バックオフ戦略の重要性の詳細については、 AWS データベースブログの[「ベストプラクティス」ブログ投稿](https://aws.amazon.com/blogs/database/best-practices-redis-clients-and-amazon-elasticache-for-redis/)の「バックオフロジック」セクションを参照してください。

```
ClientResources clientResources = DefaultClientResources.builder()
   .addressResolverGroup(new DirContextDnsResolver())
    .reconnectDelay(
        Delay.fullJitter(
            Duration.ofMillis(100),     // minimum 100 millisecond delay
            Duration.ofSeconds(10),      // maximum 10 second delay
            100, TimeUnit.MILLISECONDS)) // 100 millisecond base
    .build();
```

**Timeouts **

コマンドのタイムアウトよりも低い接続タイムアウト値を使用してください。Lettuce はレイジー接続確立を使用します。そのため、接続タイムアウトがコマンドタイムアウトよりも大きい場合、Lettuce が異常なノードへの接続を試みてコマンドのタイムアウトが常に超過すると、トポロジ更新後に障害が一定期間持続する可能性があります。

異なるコマンドに対しては動的コマンドタイムアウトを使用してください。コマンドの想定期間に基づいてコマンドタイムアウトを設定することをお勧めします。例えば、FLUSHDB、FLUSHALL、KEYS、SMEMBERS、Lua スクリプトなど、複数のキーを反復処理するコマンドにはタイムアウトを長く設定します。SET、GET、HSET など、1 つのキーコマンドではタイムアウトを短くします。

**注記**  
次の例で設定されているタイムアウトは、最大 20 バイトの長さのキーと値で SET/GET コマンドを実行したテスト用です。コマンドが複雑な場合や、キーと値が大きい場合は、処理時間が長くなる可能性があります。タイムアウトは、アプリケーションのユースケースに基づいて設定する必要があります。

```
private static final Duration META_COMMAND_TIMEOUT = Duration.ofMillis(1000);
private static final Duration DEFAULT_COMMAND_TIMEOUT = Duration.ofMillis(250);
// Socket connect timeout should be lower than command timeout for Lettuce
private static final Duration CONNECT_TIMEOUT = Duration.ofMillis(100);
    
SocketOptions socketOptions = SocketOptions.builder()
    .connectTimeout(CONNECT_TIMEOUT)
    .build();
 

class DynamicClusterTimeout extends TimeoutSource {
     private static final Set<ProtocolKeyword> META_COMMAND_TYPES = ImmutableSet.<ProtocolKeyword>builder()
          .add(CommandType.FLUSHDB)
          .add(CommandType.FLUSHALL)
          .add(CommandType.CLUSTER)
          .add(CommandType.INFO)
          .add(CommandType.KEYS)
          .build();

    private final Duration defaultCommandTimeout;
    private final Duration metaCommandTimeout;

    DynamicClusterTimeout(Duration defaultTimeout, Duration metaTimeout)
    {
        defaultCommandTimeout = defaultTimeout;
        metaCommandTimeout = metaTimeout;
    }

    @Override
    public long getTimeout(RedisCommand<?, ?, ?> command) {
        if (META_COMMAND_TYPES.contains(command.getType())) {
            return metaCommandTimeout.toMillis();
        }
        return defaultCommandTimeout.toMillis();
    }
}

// Use a dynamic timeout for commands, to avoid timeouts during
// cluster management and slow operations.
TimeoutOptions timeoutOptions = TimeoutOptions.builder()
.timeoutSource(
    new DynamicClusterTimeout(DEFAULT_COMMAND_TIMEOUT, META_COMMAND_TIMEOUT))
.build();
```