Connect to a Tair (Redis OSS-compatible) instance over TLS (SSL) to encrypt data in transit and protect data integrity. Any Redis-protocol-compatible client works.
Prerequisites
Before you begin, make sure you have:
TLS (SSL) encryption enabled on the instance. See Enable TLS encryption.
An Elastic Compute Service (ECS) instance in the same virtual private cloud (VPC) as the Tair instance.
The ECS internal IP address added to the instance whitelist. See Configure whitelists.
The CA certificate (
ApsaraDB-CA-Chain.pemor.jks) downloaded from the TLS encryption page. See Enable TLS encryption.
Gather connection details
Before writing any code, collect the following from the console:
| Detail | Where to find it |
|---|---|
| Endpoint | Instances page > click the instance ID > Connection Information section. Use the VPC endpoint for lower latency and higher security. See View endpoints. |
| Port | Default is 6379. See Change an endpoint or port. |
| Account | Default account is named after the instance ID (e.g., r-bp10noxlhcoim2****). See Create and manage accounts. |
| Password | Default account: password only. Custom account: <user>:<password> (e.g., testaccount:Rp829dlwa). See Change or reset the password. |
If password-free access over VPC is enabled, clients in the same VPC can connect without a password.
Proxy connection mode
Use these examples for instances running the standard architecture, cluster architecture with proxy mode, or read/write splitting architecture.
redis-cli
Build redis-cli with BUILD_TLS=yes to enable TLS support.
Log in to the ECS instance and run the following commands to download, compile, and install redis-cli. Compilation typically takes 2 to 3 minutes.
sudo yum -y install openssl-devel gcc # Install build dependencies wget https://download.redis.io/releases/redis-7.2.0.tar.gz tar xzf redis-7.2.0.tar.gz cd redis-7.2.0 && make BUILD_TLS=yesThis example uses Redis 7.2.0. Other versions are also supported.
Connect to the instance, specifying the CA certificate path after
--cacert:./src/redis-cli -h r-bp14joyeihew30****.redis.rds.aliyuncs.com -p 6379 --tls --cacert ./ApsaraDB-CA-Chain.pemAuthenticate:
AUTH passwordAn
OKresponse confirms the connection.
Java (Jedis)
All examples use a JKS-format TrustStore to load the CA certificate. Create a helper method to build the SSLSocketFactory, then pass it to the connection pool or cluster client.
Use Jedis (latest version).
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisSSLTest {
private static SSLSocketFactory createTrustStoreSSLSocketFactory(String jksFile) throws Exception {
KeyStore trustStore = KeyStore.getInstance("jks");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(jksFile);
trustStore.load(inputStream, null);
} finally {
inputStream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
}
public static void main(String[] args) throws Exception {
// ApsaraDB-CA-Chain.jks is the certificate file name.
final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory("ApsaraDB-CA-Chain.jks");
// Configure the connection pool with the instance endpoint, port, timeout, and password.
JedisPool pool = new JedisPool(new GenericObjectPoolConfig(), "r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com",
6379, 2000, "redistest:Pas***23", 0, true, sslSocketFactory, null, null);
try (Jedis jedis = pool.getResource()) {
jedis.set("key", "value");
System.out.println(jedis.get("key"));
}
}
}Python (redis-py)
Both connection pool and standard connection approaches use ssl=True and point to the PEM certificate file.
Use redis-py (latest version).
Connection pool
#!/bin/python
import redis
# ApsaraDB-CA-Chain.pem is the certificate file name.
# Replace host, port, and password with your instance values.
pool = redis.ConnectionPool(
connection_class=redis.connection.SSLConnection,
max_connections=100,
host="r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com",
port=6379,
password="redistest:Pas***23",
ssl_cert_reqs=True,
ssl_ca_certs="ApsaraDB-CA-Chain.pem"
)
client = redis.Redis(connection_pool=pool)
client.set("hi", "redis")
print(client.get("hi"))Standard connection
#!/bin/python
import redis
# ApsaraDB-CA-Chain.pem is the certificate file name.
# Replace host, port, and password with your instance values.
client = redis.Redis(
host="r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com",
port=6379,
password="redistest:Test1234",
ssl=True,
ssl_cert_reqs="required",
ssl_ca_certs="ApsaraDB-CA-Chain.pem"
)
client.set("hello", "world")
print(client.get("hello"))PHP (predis)
Use predis (latest version). If you prefer phpredis, see phpredis/phpredis#1600 for a connection example.
<?php
require __DIR__.'/predis/autoload.php';
/* ApsaraDB-CA-Chain.pem is the certificate file name.
Replace host, port, and password with your instance values. */
$client = new Predis\Client([
'scheme' => 'tls',
'host' => 'r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com',
'port' => 6379,
'password' => 'redistest:Pas***23',
'ssl' => ['cafile' => 'ApsaraDB-CA-Chain.pem', 'verify_peer' => true],
]);
$client->set("hello", "world");
print $client->get("hello")."\n";
?>C# (StackExchange.Redis)
Use StackExchange.Redis (latest version).
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using StackExchange.Redis;
namespace SSLTest
{
class Program
{
private static bool CheckServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var ca = new X509Certificate2(
"/your path/ApsaraDB-CA-Chain/ApsaraDB-CA-Chain.pem");
return chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint == ca.Thumbprint);
}
static void Main(string[] args)
{
// ApsaraDB-CA-Chain.pem is the certificate file name.
// Replace host, port, and password with your instance values.
ConfigurationOptions config = new ConfigurationOptions()
{
EndPoints = {"r-bp10q23zyfriodu*****.redis.rds.aliyuncs.com:6379"},
Password = "redistest:Pas***23",
Ssl = true,
};
config.CertificateValidation += CheckServerCertificate;
using (var conn = ConnectionMultiplexer.Connect(config))
{
Console.WriteLine("connected");
var db = conn.GetDatabase();
db.StringSet("hello", "world");
Console.WriteLine(db.StringGet("hello"));
}
}
}
}Spring Data Redis
Use Spring Data Redis 2.7.12 (for Java 1.8) or later.
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// Store TLS certificate configuration in a properties file for production use.
String host = "r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com";
int port = 6379;
String password = "Pas***23";
String trustStoreFilePath = "/path/to/ApsaraDB-CA-Chain.jks";
ClientOptions clientOptions = ClientOptions.builder().sslOptions(
SslOptions.builder().jdkSslProvider().truststore(new File(trustStoreFilePath)).build()).build();
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
config.setPassword(password);
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.useSsl().build();
return new LettuceConnectionFactory(config, lettuceClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
}
}Lettuce
Use Lettuce 6.2.4.RELEASE or later.
public class SSLExample {
public static void main(String[] args) throws Exception {
String host = "r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com";
int port = 6379;
String password = "Pas***23";
String trustStoreFilePath = "/path/to/ApsaraDB-CA-Chain.jks";
RedisURI uri = RedisURI.builder()
.withHost(host)
.withPort(port)
.withPassword(password.toCharArray())
.withSsl(true).build();
SslOptions sslOptions = SslOptions.builder()
.jdkSslProvider()
.truststore(new File(trustStoreFilePath)).build();
ClientOptions clientOptions = ClientOptions.builder()
.sslOptions(sslOptions).build();
RedisClient client = RedisClient.create(uri);
client.setOptions(clientOptions);
RedisCommands<String, String> sync = client.connect().sync();
System.out.println(sync.set("key", "value"));
System.out.println(sync.get("key"));
}
}Go (go-redis)
Use go-redis v9.0 or later.
The Go example uses a custom VerifyPeerCertificate function to validate the CA chain while skipping hostname verification. This is necessary because the Tair endpoint hostname may not match the certificate's common name.
package main
import (
"context"
"fmt"
"io/ioutil"
"crypto/tls"
"crypto/x509"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
caCert, err := ioutil.ReadFile("/root/ApsaraDB-CA-Chain.pem")
if err != nil {
fmt.Println("Error loading CA certificate:", err)
return
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
RootCAs: caCertPool,
InsecureSkipVerify: true, // Not actually skipping — cert is verified in VerifyPeerCertificate
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// Validate the server certificate against the CA chain,
// skipping hostname verification (not supported in direct connection mode).
// Reference: https://github.com/golang/go/issues/21971#issuecomment-412836078
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
panic(err)
}
certs[i] = cert
}
opts := x509.VerifyOptions{
Roots: caCertPool,
DNSName: "", // Skip hostname verification
Intermediates: x509.NewCertPool(),
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
},
}
rdb := redis.NewClient(&redis.Options{
Addr: "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379",
Username: "default",
Password: "Pas***23",
TLSConfig: tlsConfig,
})
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)
}Direct connection mode
Use these examples only for instances running the cluster architecture with direct connection mode.
In direct connection mode, the client connects directly to individual cluster shards. For Jedis and Spring Data Redis (Jedis), keep the total connections per shard below the shard's maximum connection limit: number of application servers × MaxTotal < max connections per shard.redis-cli
Build redis-cli with BUILD_TLS=yes, same as for proxy connection mode.
Log in to the ECS instance and compile redis-cli:
sudo yum -y install openssl-devel gcc # Install build dependencies wget https://download.redis.io/releases/redis-7.2.0.tar.gz tar xzf redis-7.2.0.tar.gz cd redis-7.2.0 && make BUILD_TLS=yesConnect using the
-cflag (required for cluster mode):./src/redis-cli -h r-bp14joyeihew30****.redis.rds.aliyuncs.com -p 6379 -c --tls --cacert ./ApsaraDB-CA-Chain.pemAuthenticate:
AUTH passwordAn
OKresponse confirms the connection.
Java (JedisCluster)
Use Jedis (latest version).
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.HashSet;
import java.util.Set;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import redis.clients.jedis.ConnectionPoolConfig;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class JedisClusterTSL {
private static final int DEFAULT_TIMEOUT = 2000;
private static final int DEFAULT_REDIRECTIONS = 5;
private static final ConnectionPoolConfig jedisPoolConfig = new ConnectionPoolConfig();
private static SSLSocketFactory createTrustStoreSSLSocketFactory(String jksFile) throws Exception {
KeyStore trustStore = KeyStore.getInstance("jks");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(jksFile);
trustStore.load(inputStream, null);
} finally {
inputStream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
}
public static void main(String args[]) throws Exception {
// In direct connection mode, keep (business machines × MaxTotal) below the per-shard connection limit.
jedisPoolConfig.setMaxTotal(30);
jedisPoolConfig.setMaxIdle(30);
jedisPoolConfig.setMinIdle(15);
int port = 6379;
String host = "r-2zee50zxi5iiq****.redis.rds-aliyun.rds.aliyuncs.com";
String user = "default";
String password = "Pas***23";
final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory("/root/ApsaraDB-CA-Chain.jks");
DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder()
.connectionTimeoutMillis(DEFAULT_TIMEOUT)
.socketTimeoutMillis(DEFAULT_TIMEOUT)
.user(user).password(password)
.ssl(true)
.sslSocketFactory(sslSocketFactory).build();
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort(host, port));
JedisCluster jc = new JedisCluster(jedisClusterNode, jedisClientConfig, DEFAULT_REDIRECTIONS, jedisPoolConfig);
System.out.println(jc.set("key", "value"));
System.out.println(jc.get("key"));
jc.close(); // Call when the application exits to release resources.
}
}Python (redis-py)
Use redis-py 4.3.6 (for Python 3.6) or later.
#!/usr/bin/env python
from redis.cluster import RedisCluster
# Replace host and port with your instance endpoint and port.
host = 'r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com'
port = 6379
# Replace user and pwd with your instance account and password.
user = 'default'
pwd = 'Pas***23'
rc = RedisCluster(host=host, port=port, username=user, password=pwd,
ssl=True, ssl_ca_certs="/root/ApsaraDB-CA-Chain.pem")
rc.set('foo', 'bar')
print(rc.get('foo'))PHP (phpredis)
Use phpredis 5.3.7 or later.
<?php
// Direct connection endpoint and port.
$array = ['r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379'];
// Connection password.
$pwd = "Pas***23";
// TLS settings.
$tls = ["verify_peer" => false, "verify_peer_name" => false];
// Connect to the cluster.
$obj_cluster = new RedisCluster(NULL, $array, 1.5, 1.5, true, $pwd, $tls);
var_dump($obj_cluster);
if ($obj_cluster->set("foo", "bar") == false) {
die($obj_cluster->getLastError());
}
$value = $obj_cluster->get("foo");
echo $value;
echo "\n";
?>C# (StackExchange.Redis)
Use StackExchange.Redis (latest version).
using StackExchange.Redis;
using System;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace TairClient
{
class Program
{
static void Main()
{
const string Host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com";
const int Port = 6379;
Console.WriteLine("connecting...");
var config = new ConfigurationOptions
{
EndPoints = { { Host, Port } },
Ssl = true,
Password = "Pas***23",
};
config.CertificateValidation += (sender, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.RemoteCertificateChainErrors ||
errors == SslPolicyErrors.RemoteCertificateNameMismatch)
{
return true;
}
var caCert = LoadCertificateFromPem("/root/ApsaraDB-CA-Chain.pem");
var isCertIssuedByTrustedCA = chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint.Equals(
caCert.Thumbprint, StringComparison.OrdinalIgnoreCase));
return isCertIssuedByTrustedCA;
};
using (var conn = ConnectionMultiplexer.Connect(config))
{
Console.WriteLine("connected");
var db = conn.GetDatabase();
db.StringSet("hello", "world");
Console.WriteLine(db.StringGet("hello")); // world
}
}
private static X509Certificate2 LoadCertificateFromPem(string pemFilePath)
{
X509Certificate2 cert = X509Certificate2.CreateFromPem(File.ReadAllText(pemFilePath));
return cert;
}
}
}Spring Data Redis
Use Spring Data Redis 2.7.5 (for Java 1.8) or later. Both Jedis and Lettuce are supported.
With Jedis (recommended)
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class RedisConfigJedis {
private static SSLSocketFactory createTrustStoreSSLSocketFactory(String jksFile) throws Exception {
KeyStore trustStore = KeyStore.getInstance("jks");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(jksFile);
trustStore.load(inputStream, null);
} finally {
inputStream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
}
@Bean
public RedisConnectionFactory redisConnectionFactory() throws Exception {
String host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379";
String user = "default";
String password = "Pas***23";
String trustStoreFilePath = "/root/ApsaraDB-CA-Chain.jks";
List<String> clusterNodes = Arrays.asList(host);
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodes);
redisClusterConfiguration.setUsername(user);
redisClusterConfiguration.setPassword(password);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// In direct connection mode, keep (business machines × MaxTotal) below the per-shard connection limit.
jedisPoolConfig.setMaxTotal(30);
jedisPoolConfig.setMaxIdle(20);
jedisPoolConfig.setMinIdle(20);
final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory(trustStoreFilePath);
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.useSsl().sslSocketFactory(sslSocketFactory)
.and().usePooling().poolConfig(jedisPoolConfig).build();
return new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}With Lettuce
import java.io.File;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.SslOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
String host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com";
int port = 6379;
String user = "default";
String password = "Pas***23";
String trustStoreFilePath = "/root/ApsaraDB-CA-Chain.jks";
ClientOptions clientOptions = ClientOptions.builder().sslOptions(
SslOptions.builder().jdkSslProvider().truststore(new File(trustStoreFilePath)).build()).build();
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
clusterConfiguration.clusterNode(host, port);
clusterConfiguration.setUsername(user);
clusterConfiguration.setPassword(password);
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.useSsl()
.disablePeerVerification()
.build();
return new LettuceConnectionFactory(clusterConfiguration, lettuceClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}Lettuce (cluster mode)
Use Lettuce 6.3.0.RELEASE or later. This example enables TCP keepalive and topology refresh for production reliability.
import java.io.File;
import java.time.Duration;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.SocketOptions.KeepAliveOptions;
import io.lettuce.core.SocketOptions.TcpUserTimeoutOptions;
import io.lettuce.core.SslOptions;
import io.lettuce.core.SslVerifyMode;
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;
public class SSLClusterExample {
/**
* TCP keepalive settings:
* TCP_KEEPIDLE = 30s, TCP_KEEPINTVL = 10s, TCP_KEEPCNT = 3
*/
private static final int TCP_KEEPALIVE_IDLE = 30;
/**
* TCP_USER_TIMEOUT prevents Lettuce from hanging indefinitely on broken connections.
* See: 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 {
String host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com";
int port = 6379;
String password = "Pas***23";
String trustStoreFilePath = "/root/ApsaraDB-CA-Chain.jks";
RedisURI uri = RedisURI.builder()
.withHost(host)
.withPort(port)
.withPassword(password.toCharArray())
.withSsl(true)
// Direct cluster connections require CA-only verification; FULL mode is not supported.
.withVerifyPeer(SslVerifyMode.CA)
.build();
SslOptions sslOptions = SslOptions.builder()
.jdkSslProvider()
.truststore(new File(trustStoreFilePath)).build();
ClusterTopologyRefreshOptions refreshOptions = ClusterTopologyRefreshOptions.builder()
.enablePeriodicRefresh(Duration.ofSeconds(15))
.dynamicRefreshSources(false)
.enableAllAdaptiveRefreshTriggers()
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(15)).build();
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();
RedisClusterClient redisClient = RedisClusterClient.create(uri);
redisClient.setOptions(ClusterClientOptions.builder()
.socketOptions(socketOptions)
.sslOptions(sslOptions)
.validateClusterNodeMembership(false)
.topologyRefreshOptions(refreshOptions).build());
StatefulRedisClusterConnection<String, String> connection = redisClient.connect();
connection.sync().set("key", "value");
System.out.println(connection.sync().get("key"));
}
}Go (go-redis)
Use go-redis v9.0 or later. The cluster client uses NewClusterClient instead of NewClient.
package main
import (
"context"
"fmt"
"io/ioutil"
"crypto/tls"
"crypto/x509"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
caCert, err := ioutil.ReadFile("/root/ApsaraDB-CA-Chain.pem")
if err != nil {
fmt.Println("Error loading CA certificate:", err)
return
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
RootCAs: caCertPool,
InsecureSkipVerify: true, // Not actually skipping — cert is verified in VerifyPeerCertificate
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// Validate the CA chain while skipping hostname verification.
// Reference: https://github.com/golang/go/issues/21971#issuecomment-412836078
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
panic(err)
}
certs[i] = cert
}
opts := x509.VerifyOptions{
Roots: caCertPool,
DNSName: "", // Skip hostname verification
Intermediates: x509.NewCertPool(),
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
},
}
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379"},
Username: "default",
Password: "Pas***23",
TLSConfig: tlsConfig,
})
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)
}FAQ
Why do I get `No subject alternative DNS name matching xxx found`?
This error occurs when you change the instance endpoint or port after enabling TLS — the existing certificate no longer matches the new address. Update the TLS certificate in the console, then reconnect.