直接接続モードが有効になっているTair (Redis OSS-compatible) クラスターインスタンスを購入した場合、ネイティブRedisクラスターをクラスターインスタンスにシームレスに移行できます。 Tair (Redis OSS-compatible) インスタンスの直接接続モードは、ネイティブRedis Clusterプロトコルをサポートしています。 このモードでは、クライアントはRedisサーバーに直接接続できるため、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
などのエラーメッセージを表示することがあります。 詳細については、「インスタンスの設定の変更」をご参照ください。 リクエストを正常に実行するには、クライアントの再試行メカニズムを設定します。 詳細については、「Tairクライアントの再試行メカニズム」をご参照ください。直接接続モードでは、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 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. config.setMaxTotal(30); // Specify the maximum number of idle connections based on your business requirements. 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 requirements. 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
/** * 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; @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(15)) .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); }
次の表に、ClusterTopologyRefreshOptions.builderのパラメーターを示します。
パラメーター
説明
例 (推奨値)
enablePeriodicRefresh (期間リフレッシュ期間)
クラスターインスタンスのトポロジが更新される間隔。 間隔を15秒に設定することを推奨します。 間隔を小さい値に設定すると、クラスターインスタンスのノードに対して多数の呼び出しが行われます。 これにより、クラスターインスタンスのパフォーマンスが低下します。
15s
dynamicRefreshSources()
クラスタインスタンスのトポロジを更新するために呼び出されるノードとして、トポロジからIPアドレスを取得したノードを使用するかどうかを指定します。 Tairインスタンスに接続するには、パラメーターをfalseに設定する必要があります。 通常、 TairインスタンスはVIPを使用します。 インスタンスをゾーン間で移行すると、インスタンスのVIPがすべて変更されます。 この場合、ルートは更新できません。 パラメーターをfalseに設定した場合、システムはAlibaba Cloudが提供するドメイン名を使用してクラスターインスタンスのノードを照会します。 ドメイン名サービスは自動的に負荷分散を実行し、 Tairインスタンスのノードにリクエストを送信します。
false
enableAllAdaptiveRefreshTriggers()
すべてのトリガーを有効にしてトポロジを適応的に更新するかどうかを指定します。 アダプティブリフレッシュを有効にすると、MOVEDリダイレクトなどのランタイムイベントがクラスターインスタンスで発生するたびに、クラスターインスタンスのトポロジが自動的にリフレッシュされます。
非該当
adaptiveRefreshTriggersTimeout(Duration timeout)
クラスターインスタンスのトポロジを1回だけ更新できる期間。 これにより、トポロジが頻繁にリフレッシュされるのを防ぎます。
15s
validateClusterNodeMembership()
クラスターノードのメンバーシップを検証するかどうかを指定します。 Alibaba Cloud Tairインスタンスの検証は不要です。
false
.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以降の使用を推奨します。 この例では、レタス6.3.0が使用されます。
次の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(15)) .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
ClusterTopologyRefreshOptions.builderのパラメーターの詳細については、前述の「Spring Data Redis」セクションの説明を参照してください。
関連ドキュメント
直接接続モードは、単純なアプリケーションシナリオに適しています。 プロキシモードは、スケーラビリティと高可用性を向上させます。 詳細については、「プロキシノードの機能」をご参照ください。
よくある質問
詳細については、「一般的なエラーとトラブルシューティング」をご参照ください。