直接接続モードが有効になっているTair (Redis OSS-compatible) クラスターインスタンスを購入した場合、ネイティブRedisクラスターをクラスターインスタンスにシームレスに移行できます。 Tair (Redis OSS-compatible) インスタンスのプライベートエンドポイントは、ネイティブRedis Clusterプロトコルをサポートしています。 直接接続モードでは、クライアントはデータサーバーに直接接続できるため、Tair (Redis OSS互換) からの応答が速くなります。
前提条件
クラスターインスタンスの直接接続モードが有効になっています。 詳細については、「直接接続モードの有効化」をご参照ください。
クラスターインスタンスのホワイトリストにクライアントIPアドレスが追加されます。 詳細については、「ホワイトリストの設定」をご参照ください。
JedisやPhpRedisなど、Redis Clusterプロトコルをサポートするクライアントが使用されます。
説明Redis Clusterプロトコルをサポートしていないクライアントを使用している場合、クライアントはリクエストを正しいシャードにリダイレクトできないため、データを取得できない場合があります。
Redis公式Webサイトの [クライアント] ページから、Redisクラスタープロトコルをサポートするクライアントのリストを取得できます。
クライアントがデプロイされているElastic Compute Service (ECS) インスタンスとクラスターインスタンスは、同じ仮想プライベートクラウド (VPC) に属しています。
背景情報
クラスターインスタンスの直接接続モードを有効にすると、Tair (Redis OSS-compatible) はクラスターインスタンス内の各データシャードのマスターノードに仮想IPアドレス (VIP) を割り当てます。 詳細については、「直接接続モードの有効化」をご参照ください。 クライアントが最初の要求をプライベートエンドポイントに送信する前に、クライアントはドメインネームシステム (DNS) サーバーを使用してプライベートエンドポイントを解決します。 解決結果は、クラスターインスタンス内のランダムデータシャードのVIPです。 クライアントはこのVIPを使用して、Redis Clusterプロトコルに基づいてインスタンスデータを管理できます。 次の図は、直接接続モードのクラスターインスタンスのアーキテクチャを示しています。
使用上の注意
異なるアーキテクチャのインスタンスは、ネイティブRedisコマンドに対して異なるレベルのサポートを提供します。 たとえば、クラスタアーキテクチャではLuaスクリプトに制限があります。 詳細については、「クラスターインスタンスおよび読み書き分離インスタンスでサポートされているコマンドの制限」をご参照ください。
直接接続モードでクラスターインスタンスの設定を変更すると、スロット移行が実行されます。 この場合、クライアントが移行中のスロットにアクセスすると、クライアントは
[移動]や[TRYAGAIN]などのエラーメッセージを返すことがあります。 詳細については、「インスタンスの設定の変更」をご参照ください。 リクエストの実行を成功させるには、クライアントの再試行メカニズムを設定します。 詳細については、「クライアントの再試行メカニズム」をご参照ください。直接接続モードでは、SELECTコマンドを使用して目的のデータベースに切り替えることができます。 ただし、StackExchange.Redisなどの特定のRedisクラスタークライアントは、SELECTコマンドをサポートしていません。 これらのクライアントのいずれかで作業する場合、DB0のみを使用できます。
プライベートエンドポイントは、Alibaba Cloud内部ネットワーク経由でのみクラスターインスタンスにアクセスできます。 クラスターインスタンスのプライベートエンドポイントを使用してインスタンスにアクセスする場合、パスワード不要アクセスとアカウントおよびパスワード認証がサポートされます。
redis-cli
プライベートエンドポイントを使用して、redis-cliからクラスターインスタンスに接続します。
プライベートエンドポイントを使用してredis-cliからクラスターインスタンスに接続する場合、コマンドに-cパラメーターを追加する必要があります。 そうしないと、接続は失敗します。
./redis-cli -h r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com -p 6379 -cデータベースアカウントのパスワードを確認します。
AUTH testaccount:Rp829dlwa詳細については、「redis-cliを使用したインスタンスへの接続」をご参照ください。
ジェディス
この例では、Jedis 4.3.0が使用されます。 詳細については、『GitHub』をご参照ください。
カスタム接続プール (推奨)
import redis.clients.jedis.*; import java.util.HashSet; import java.util.Set; public class DirectTest { private static final int DEFAULT_TIMEOUT = 2000; private static final int DEFAULT_REDIRECTIONS = 5; private static final ConnectionPoolConfig config = new ConnectionPoolConfig(); public static void main(String args[]) { // Specify the maximum number of connections. In direct connection mode, the client directly connects to a shard of a cluster instance. Therefore, the following requirement must be met: Number of clients × Value of MaxTotal < Maximum number of connections to a single shard. config.setMaxTotal(30); // Specify the maximum number of idle connections based on your business needs. config.setMaxIdle(20); config.setMinIdle(15); // Specify the private endpoint that is allocated to the cluster instance. String host = "r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com"; int port = 6379; // Specify the password that is used to connect to the cluster instance. String password = "xxxxx"; Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>(); jedisClusterNode.add(new HostAndPort(host, port)); JedisCluster jc = new JedisCluster(jedisClusterNode, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, DEFAULT_REDIRECTIONS, password, "clientName", config); jc.set("key", "value"); jc.get("key"); jc.close(); // If the application exits and you want to destroy the resources, call this method. This way, the connection is closed, and the resources are released. } }デフォルト接続プール
import redis.clients.jedis.ConnectionPoolConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import java.util.HashSet; import java.util.Set; public class DirectTest{ private static final int DEFAULT_TIMEOUT = 2000; private static final int DEFAULT_REDIRECTIONS = 5; private static final ConnectionPoolConfig DEFAULT_CONFIG = new ConnectionPoolConfig(); public static void main(String args[]){ // Specify the private endpoint that is allocated to the cluster instance. String host = "r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com"; int port = 6379; String password = "xxxx"; Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>(); jedisClusterNode.add(new HostAndPort(host, port)); JedisCluster jc = new JedisCluster(jedisClusterNode, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, DEFAULT_REDIRECTIONS,password, "clientName", DEFAULT_CONFIG); jc.set("key","value"); jc.get("key"); jc.close(); // If the application exits and you want to destroy the resources, call this method. This way, the connection is closed, and the resources are released. } }
PhpRedis
この例では、PhpRedis 5.3.7が使用されます。 詳細については、『GitHub』をご参照ください。
<?php
// Specify the private endpoint and port number that are used to connect to the cluster instance.
$array = ['r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com:6379'];
// Specify the password that is used to connect to the cluster instance.
$pwd = "xxxx";
// Use the password to connect to the cluster instance.
$obj_cluster = new RedisCluster(NULL, $array, 1.5, 1.5, true, $pwd);
// Display the result of the connection.
var_dump($obj_cluster);
if ($obj_cluster->set("foo", "bar") == false) {
die($obj_cluster->getLastError());
}
$value = $obj_cluster->get("foo");
echo $value;
?>redis-py
この例では、Python 3.9とredis-py 4.4.1が使用されています。 詳細については、『GitHub』をご参照ください。
# !/usr/bin/env python
# -*- coding: utf-8 -*-
from redis.cluster import RedisCluster
# Replace the values of the host and port parameters with the endpoint and port number that are used to connect to the instance.
host = 'r-bp10noxlhcoim2****.redis.rds.aliyuncs.com'
port = 6379
# Replace the values of the user and pwd parameters with the username and password that are used to connect to the instance.
user = 'testaccount'
pwd = 'Rp829dlwa'
rc = RedisCluster(host=host, port=port, username=user, password=pwd)
# You can perform operations on the instance after the connection is established. The following code provides an example on how to call the set and get methods:
rc.set('foo', 'bar')
print(rc.get('foo'))SpringデータRedis
次のサンプルプロジェクトは、Mavenを使用して作成されます。 LettuceまたはJedisクライアントを手動でダウンロードすることもできます。
次のMaven依存関係を追加します。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.aliyun.tair</groupId> <artifactId>spring-boot-example</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-example</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>6.3.0.RELEASE</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-transport-native-epoll</artifactId> <version>4.1.100.Final</version> <classifier>linux-x86_64</classifier> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>Spring Data Redisエディタに次のコードを入力し、コメントに基づいてコードを変更します。
この例では、Spring Data Redis 2.4.2が使用されます。
(推奨) Jedisを使用したSpring Data Redis
@Bean JedisConnectionFactory redisConnectionFactory() { List<String> clusterNodes = Arrays.asList("r-bp10noxlhcoim2****.redis.rds.aliyuncs.com:6379"); RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodes); redisClusterConfiguration.setUsername("user"); redisClusterConfiguration.setPassword("password"); JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // Specify the maximum number of idle connections. In direct connection mode, the client directly connects to a shard of a cluster instance. Therefore, the following requirement must be met: Number of clients × Value of MaxTotal < Maximum number of connections to a single shard. jedisPoolConfig.setMaxTotal(30); // Specify the maximum number of idle connections based on your business needs. jedisPoolConfig.setMaxIdle(20); // Disable testOn[Borrow|Return] to prevent generating additional ping commands. jedisPoolConfig.setTestOnBorrow(false); jedisPoolConfig.setTestOnReturn(false); return new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig); }レタスと春のデータRedis
警告レタスのデフォルト設定では、インスタンスが変更されたときに、アプリケーションの待ち時間が増加したり、アクセス不能になったりする場合があります。 レタスを適切に設定するには、レタス関連のパラメーターの説明をよくお読みください。
レタスのバージョンは6.3.0.RELEASE以降でなければなりません。 詳細については、「 [お知らせ] レタスのアップグレードの提案」をご参照ください。
/** * Enable TCP keepalive and configure the following three parameters: * TCP_KEEPIDLE = 30 * TCP_KEEPINTVL = 10 * TCP_KEEPCNT = 3 */ private static final int TCP_KEEPALIVE_IDLE = 30; /** * You can use the TCP_USER_TIMEOUT parameter to avoid scenarios in which Lettuce remains stuck in a continuous timeout loop during a failure or crash event. * refer: https://github.com/lettuce-io/lettuce-core/issues/2082 */ private static final int TCP_USER_TIMEOUT = 30; @Bean public LettuceConnectionFactory redisConnectionFactory() { List<String> clusterNodes = Arrays.asList("r-bp10noxlhcoim2****.redis.rds.aliyuncs.com:6379"); RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodes); redisClusterConfiguration.setUsername("user"); redisClusterConfiguration.setPassword("password"); // Config TCP KeepAlive SocketOptions socketOptions = SocketOptions.builder() .keepAlive(KeepAliveOptions.builder() .enable() .idle(Duration.ofSeconds(TCP_KEEPALIVE_IDLE)) .interval(Duration.ofSeconds(TCP_KEEPALIVE_IDLE / 3)) .count(3) .build()) .tcpUserTimeout(TcpUserTimeoutOptions.builder() .enable() .tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT)) .build()) .build(); ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(Duration.ofSeconds(60)) .dynamicRefreshSources(false) .enableAllAdaptiveRefreshTriggers() .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(15)).build(); LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder(). clientOptions(ClusterClientOptions.builder() .socketOptions(socketOptions) .validateClusterNodeMembership(false) .topologyRefreshOptions(topologyRefreshOptions).build()).build(); return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration); }
.NET
この例では、NET 6.0とStackExchange.Redis 2.6.90が使用されます。
using StackExchange.Redis;
class RedisConnSingleton {
// Specify the endpoint, port number, username, and password that are used to connect to the cluster instance.
private static ConfigurationOptions configurationOptions = ConfigurationOptions.Parse("r-bp10noxlhcoim2****.redis.rds.aliyuncs.com:6379,user=testaccount,password=Rp829dlwa,connectTimeout=2000");
//the lock for singleton
private static readonly object Locker = new object();
//singleton
private static ConnectionMultiplexer redisConn;
//singleton
public static ConnectionMultiplexer getRedisConn()
{
if (redisConn == null)
{
lock (Locker)
{
if (redisConn == null || !redisConn.IsConnected)
{
redisConn = ConnectionMultiplexer.Connect(configurationOptions);
}
}
}
return redisConn;
}
}
class Program
{
static void Main(string[] args)
{
ConnectionMultiplexer cm = RedisConnSingleton.getRedisConn();
var db = cm.GetDatabase();
db.StringSet("key", "value");
String ret = db.StringGet("key");
Console.WriteLine("get key: " + ret);
}
}ノード-redis
この例では、Node.js 19.4.0とnode-redis 4.5.1が使用されています。
import { createCluster } from 'redis';
// Specify the endpoint, port number, username, and password that are used to connect to the instance.
// After you supply the username and password for the url parameter, you must also supply the global username and password for the defaults parameter.
// The global username and password are used to authenticate the remaining nodes. If the remaining nodes are not authenticated, the NOAUTH error occurs.
const cluster = createCluster({
rootNodes: [{
url: 'redis://testaccount:Rp829dlwa@r-bp10noxlhcoim2****.redis.rds.aliyuncs.com:6379'
}],
defaults: {
username: 'testaccount',
password: 'Rp829dlwa'
}
});
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
await cluster.connect();
await cluster.set('key', 'value');
const value = await cluster.get('key');
console.log('get key: %s', value);
await cluster.disconnect();
Go-redis
この例では、Go 1.19.7とgo-redis 9.5.1が使用されます。
go-redis 9.0または後で使用します。 9.0より前のgo-redisバージョンを使用すると、プライベートエンドポイントに接続するときに非互換性エラーが発生する可能性があります。 詳細については、「」「一般的なエラーとトラブルシューティング」をご参照ください。
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v9"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"r-bp10noxlhcoim2****.redis.rds.aliyuncs.com:6379"},
Username: "testaccount",
Password: "Rp829dlwa",
})
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)
}レタス
レタスのデフォルト設定では、インスタンスが変更されたときに、アプリケーションの待ち時間が増加したり、アクセス不能になったりする場合があります。 レタスを適切に設定するには、レタス関連のパラメーターの説明をよくお読みください。
レタスのバージョンは6.3.0.RELEASE以降でなければなりません。 詳細については、「 [お知らせ] レタスのアップグレードの提案」をご参照ください。
次のMaven依存関係を追加します。
<dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>6.3.0.RELEASE</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-transport-native-epoll</artifactId> <version>4.1.65.Final</version> <classifier>linux-x86_64</classifier> </dependency>次のコードを追加し、コメントに基づいてコードを変更します。
import io.lettuce.core.RedisURI; import io.lettuce.core.SocketOptions; import io.lettuce.core.cluster.ClusterClientOptions; import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; import io.lettuce.core.cluster.RedisClusterClient; import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; import java.time.Duration; public class ClusterDemo { /** * Enable TCP keepalive and configure the following three parameters: * TCP_KEEPIDLE = 30 * TCP_KEEPINTVL = 10 * TCP_KEEPCNT = 3 */ private static final int TCP_KEEPALIVE_IDLE = 30; /** * The TCP_USER_TIMEOUT parameter can avoid situations where Lettuce remains stuck in a continuous timeout loop during a failure or crash event. * refer: https://github.com/lettuce-io/lettuce-core/issues/2082 */ private static final int TCP_USER_TIMEOUT = 30; public static void main(String[] args) throws Exception { // Replace the values of host, port, and password with the actual instance information. String host = "r-bp1ln3c4kopj3l****.redis.rds.aliyuncs.com"; int port = 6379; String password = "Da****3"; RedisURI redisURI = RedisURI.Builder.redis(host) .withPort(port) .withPassword(password) .build(); ClusterTopologyRefreshOptions refreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(Duration.ofSeconds(60)) .dynamicRefreshSources(false) .enableAllAdaptiveRefreshTriggers() .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(15)).build(); // Config TCP KeepAlive SocketOptions socketOptions = SocketOptions.builder() .keepAlive(SocketOptions.KeepAliveOptions.builder() .enable() .idle(Duration.ofSeconds(TCP_KEEPALIVE_IDLE)) .interval(Duration.ofSeconds(TCP_KEEPALIVE_IDLE/3)) .count(3) .build()) .tcpUserTimeout(SocketOptions.TcpUserTimeoutOptions.builder() .enable() .tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT)) .build()) .build(); RedisClusterClient redisClient = RedisClusterClient.create(redisURI); redisClient.setOptions(ClusterClientOptions.builder() .socketOptions(socketOptions) .validateClusterNodeMembership(false) .topologyRefreshOptions(refreshOptions).build()); StatefulRedisClusterConnection<String, String> connection = redisClient.connect(); connection.sync().set("key", "value"); System.out.println(connection.sync().get("key")); } }上記のコードを実行します。 次の出力は、正常に完了すると予想されます。
value
レタス関連のパラメーターを次の表に示します。
パラメーター | デフォルト設定 | 説明 | 推奨設定 |
enablePeriodicRefresh (期間リフレッシュ期間) | Disabled | 定期的なクラスタートポロジ更新を有効にします。 | リフレッシュ期間を60秒に設定することを推奨します。 このオプションを有効にすると、非アクティブな永続接続でもローカルトポロジの最新のビューが維持されます。 |
dynamicRefreshSources(boolean dynamicRefreshSources) | true | このパラメーターがtrueに設定されている場合、Cluster nodesコマンドによって返されたすべてのノードがクラスタートポロジの更新に使用されます。 このパラメーターがfalseに設定されている場合、指定されたノードエンドポイントが使用されます。 | 特に必要がない限り、このパラメーターをfalseに設定することを推奨します。 このオプションを有効にすると、CLUSTER NODESコマンドがすべてのノードに送信され、サーバーの負荷が増加します。 さらに、構成変更中に、エンドポイントを使用してトポロジを更新すると、通常、より高速で信頼性が高くなります。 |
enableAllAdaptiveRefreshTriggers() | Disabled | MOVEDメッセージの受信時に自動クラスタートポロジ更新を有効にします。 | このパラメーターを有効にする必要があります。 このオプションを有効にすると、トポロジ変更後にLettuceがローカルトポロジを迅速に更新できるようになります。 |
adaptiveRefreshTriggersTimeout(Duration timeout) | 30 秒 | 指定されたタイムアウト期間内に1回のリフレッシュのみを許可するように、クラスタートポロジのリフレッシュの頻度を制限します。 | このパラメーターを15秒に設定することを推奨します。 クラスタ内の複数のノードにわたるトポロジ変更は、アトミックではない。 その結果、レタスによってトリガーされた最初のトポロジーリフレッシュが失敗する可能性があります。 その後の迅速な更新により、トポロジが正しく更新されます。 アプリケーションの数が少ない場合、同時にCLUSTER NODESコマンドを送信するクライアントは少なくなります。 この場合、この値を適切に減らして、トポロジテーブルの収束を高速化できます。 |
validateClusterNodeMembership(boolean validateClusterNodeMembership) | true | トポロジ変更中、LettuceはMOVEDを使用してコマンドを正しいノードにリダイレクトします。 このパラメーターをtrueに設定すると、CLUSTER nodesコマンド出力に明示的にリストされているノードにのみコマンドをリダイレクトできます。 | このパラメーターはfalseに設定する必要があります。 このパラメーターをfalseに設定すると、クラスタートポロジの変更後にローカルトポロジの更新が完了する前に、新しく追加されたノードにアクセスできます。 |
関連ドキュメント
直接接続モードは、単純なアプリケーションシナリオに適しています。 プロキシモードは、スケーラビリティと高可用性を向上させます。 詳細については、「プロキシノードの機能」をご参照ください。
よくある質問
MOVED 4578 172.18.xx.xxx:6379エラーが返されるのはなぜですか?MOVED <slot> <IP:Port>エラーは、照会されたキーがインスタンス内の別のノードにあることを示します。 ほとんどの場合、このエラーはRedis Clusterをサポートしていないクライアントが使用されている場合に発生します。 たとえば、このエラーは、他のクライアントにも当てはまる次のシナリオで発生する可能性があります。-cオプションなしでredis-cliを使用してインスタンスに接続します。Pythonでは、自動リダイレクトをサポートしていない標準の
redis-pyクライアントを使用します。 この場合、redis-clusterクライアントを使用する必要があります。
その他のエラーについては、「一般的なエラーとトラブルシューティング」をご参照ください。