All Products
Search
Document Center

Tair (Redis® OSS-Compatible):Aktifkan koneksi terenkripsi TLS (SSL) ke instans

Last Updated:Jul 18, 2026

Hubungkan ke instans Tair (kompatibel dengan Redis OSS) melalui TLS (SSL) untuk mengenkripsi data dalam transmisi dan menjaga integritas data. Semua klien yang kompatibel dengan protokol Redis dapat digunakan.

Prasyarat

Sebelum memulai, pastikan Anda telah:

  • Mengaktifkan enkripsi TLS (SSL) pada instans. Lihat Aktifkan enkripsi TLS.

  • Memiliki instans Elastic Compute Service (ECS) di virtual private cloud (VPC) yang sama dengan instans Tair.

  • Menambahkan alamat IP internal ECS ke daftar putih instans. Lihat Konfigurasi daftar putih.

  • Mengunduh sertifikat CA (ApsaraDB-CA-Chain.pem atau .jks) dari halaman enkripsi TLS. Lihat Aktifkan enkripsi TLS.

Kumpulkan detail koneksi

Sebelum menulis kode apa pun, kumpulkan informasi berikut dari Konsol:

Detail

Lokasi penemuannya

Endpoint

Halaman Instances > klik ID instans > bagian Connection Information. Gunakan endpoint VPC untuk latensi lebih rendah dan keamanan lebih tinggi. Lihat Lihat endpoint.

Port

Default adalah 6379. Lihat Ubah endpoint atau port.

Account

Akun default dinamai sesuai ID instans (misalnya, r-bp10noxlhcoim2****). Lihat Buat dan kelola akun.

Password

Akun default: hanya password. Akun kustom: <user>:<password> (misalnya, testaccount:Rp829dlwa). Lihat Ubah atau reset password.

Jika akses tanpa password melalui VPC diaktifkan, klien dalam VPC yang sama dapat terhubung tanpa password.

Modus koneksi proxy

Gunakan contoh berikut untuk instans yang menjalankan arsitektur standar, arsitektur kluster dengan modus proxy, atau arsitektur pemisahan baca/tulis.

openssl (Uji cepat)

Jika Anda hanya perlu memverifikasi secara cepat bahwa koneksi SSL/TLS berfungsi dan tidak ingin menginstal redis-cli, Anda dapat menggunakan perintah openssl s_client:

openssl s_client -connect r-bp14joyeihew30****.redis.rds.aliyuncs.com:6379 -CAfile ApsaraDB-CA-Chain.pem

Jika perintah menampilkan informasi handshake SSL dan koneksi tidak langsung ditutup, maka koneksi SSL/TLS berfungsi.

redis-cli

Buat redis-cli dengan BUILD_TLS=yes untuk mengaktifkan dukungan TLS.

  1. Login ke instans ECS dan jalankan perintah berikut untuk mengunduh, mengompilasi, dan menginstal redis-cli. Proses kompilasi biasanya memerlukan waktu 2 hingga 3 menit.

    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=yes

    Contoh ini menggunakan Redis 7.2.0. Versi lain juga didukung.

  2. Hubungkan ke instans, tentukan path sertifikat CA setelah --cacert:

    ./src/redis-cli -h r-bp14joyeihew30****.redis.rds.aliyuncs.com -p 6379 --tls --cacert ./ApsaraDB-CA-Chain.pem
  3. Autentikasi:

    AUTH password

    Tanggapan OK mengonfirmasi koneksi berhasil.

Java (Jedis)

Semua contoh menggunakan TrustStore format JKS untuk memuat sertifikat CA. Buat metode pembantu untuk membuat SSLSocketFactory, lalu berikan ke Kolam koneksi atau klien kluster.

Gunakan Jedis (versi terbaru).

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 adalah nama file sertifikat.
        final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory("ApsaraDB-CA-Chain.jks");
        // Konfigurasi kolam koneksi dengan endpoint, port, timeout, dan password instans.
        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)

Pendekatan Kolam koneksi maupun koneksi standar menggunakan ssl=True dan mengarahkan ke file sertifikat PEM.

Gunakan redis-py (versi terbaru).

Kolam koneksi

#!/bin/python
import redis

# ApsaraDB-CA-Chain.pem adalah nama file sertifikat.
# Ganti host, port, dan password dengan nilai instans Anda.
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"))

Koneksi standar

#!/bin/python
import redis

# ApsaraDB-CA-Chain.pem adalah nama file sertifikat.
# Ganti host, port, dan password dengan nilai instans Anda.
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)

Gunakan predis (versi terbaru). Jika Anda lebih suka phpredis, lihat phpredis/phpredis#1600 untuk contoh koneksi.

<?php

require __DIR__.'/predis/autoload.php';

/* ApsaraDB-CA-Chain.pem adalah nama file sertifikat.
   Ganti host, port, dan password dengan nilai instans Anda. */
$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)

Gunakan StackExchange.Redis (versi terbaru).

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 adalah nama file sertifikat.
            // Ganti host, port, dan password dengan nilai instans Anda.
            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

Gunakan Spring Data Redis 2.7.12 (untuk Java 1.8) atau versi lebih baru.

@Configuration
public class RedisConfig {
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // Simpan konfigurasi sertifikat TLS dalam file properties untuk penggunaan produksi.
        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

Gunakan Lettuce 6.2.4.RELEASE atau versi lebih baru.

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)

Gunakan go-redis v9.0 atau versi lebih baru.

Contoh Go menggunakan fungsi VerifyPeerCertificate khusus untuk memvalidasi rantai CA sekaligus melewati verifikasi hostname. Hal ini diperlukan karena hostname endpoint Tair mungkin tidak cocok dengan common name pada sertifikat.

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, // Sebenarnya tidak dilewati — sertifikat diverifikasi di VerifyPeerCertificate
        VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
            // Validasi sertifikat server terhadap rantai CA,
            // lewati verifikasi hostname (tidak didukung dalam mode koneksi langsung).
            // Referensi: 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:       "", // Lewati verifikasi hostname
                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)
}

Mode koneksi langsung

Gunakan contoh berikut hanya untuk instans yang menjalankan arsitektur kluster dengan mode koneksi langsung.

Dalam mode koneksi langsung, klien terhubung langsung ke shard kluster individual. Untuk Jedis dan Spring Data Redis (Jedis), pastikan total koneksi per shard tetap di bawah batas koneksi maksimum shard tersebut: jumlah server aplikasi × MaxTotal < koneksi maksimum per shard.

redis-cli

Buat redis-cli dengan BUILD_TLS=yes, sama seperti untuk mode koneksi proxy.

  1. Login ke instans ECS dan kompilasi 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=yes
  2. Hubungkan menggunakan flag -c (wajib untuk mode kluster):

    ./src/redis-cli -h r-bp14joyeihew30****.redis.rds.aliyuncs.com -p 6379 -c --tls --cacert ./ApsaraDB-CA-Chain.pem
  3. Autentikasi:

    AUTH password

    Tanggapan OK mengonfirmasi koneksi berhasil.

Java (JedisCluster)

Gunakan Jedis (versi terbaru).

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 {
        // Dalam mode koneksi langsung, pastikan (mesin bisnis × MaxTotal) tetap di bawah batas koneksi per shard.
        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();  // Panggil saat aplikasi keluar untuk melepas sumber daya.
    }
}

Python (redis-py)

Gunakan redis-py 4.3.6 (untuk Python 3.6) atau versi lebih baru.

#!/usr/bin/env python
from redis.cluster import RedisCluster

# Ganti host dan port dengan endpoint dan port instans Anda.
host = 'r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com'
port = 6379
# Ganti user dan pwd dengan akun dan password instans Anda.
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)

Gunakan phpredis 5.3.7 atau versi lebih baru.

<?php
// Endpoint dan port koneksi langsung.
$array = ['r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379'];
// Password koneksi.
$pwd = "Pas***23";
// Pengaturan TLS.
$tls = ["verify_peer" => false, "verify_peer_name" => false];
// Hubungkan ke kluster.
$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)

Gunakan StackExchange.Redis (versi terbaru).

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

Gunakan Spring Data Redis 2.7.5 (untuk Java 1.8) atau versi lebih baru. Baik Jedis maupun Lettuce didukung.

Dengan Jedis (direkomendasikan)

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();
        // Dalam mode koneksi langsung, pastikan (mesin bisnis × MaxTotal) tetap di bawah batas koneksi per shard.
        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;
    }
}

Dengan 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 (mode kluster)

Gunakan Lettuce 6.3.0.RELEASE atau versi lebih baru. Contoh ini mengaktifkan keepalive TCP dan refresh topologi untuk keandalan produksi.

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 {
    /**
     * Pengaturan TCP keepalive:
     * TCP_KEEPIDLE = 30s, TCP_KEEPINTVL = 10s, TCP_KEEPCNT = 3
     */
    private static final int TCP_KEEPALIVE_IDLE = 30;

    /**
     * TCP_USER_TIMEOUT mencegah Lettuce hang tak terbatas pada koneksi rusak.
     * Lihat: 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)
            // Koneksi kluster langsung memerlukan verifikasi hanya CA; mode FULL tidak didukung.
            .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)

Gunakan go-redis v9.0 atau versi lebih baru. Klien kluster menggunakan NewClusterClient alih-alih 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, // Sebenarnya tidak dilewati — sertifikat diverifikasi di VerifyPeerCertificate
        VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
            // Validasi rantai CA sekaligus lewati verifikasi hostname.
            // Referensi: 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:       "", // Lewati verifikasi hostname
                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

Mengapa saya mendapatkan error `No subject alternative DNS name matching xxx found`?

Error ini terjadi ketika Anda mengubah endpoint atau port instans setelah mengaktifkan TLS — sertifikat yang ada tidak lagi cocok dengan alamat baru. Perbarui sertifikat TLS di Konsol, lalu hubungkan kembali.

Langkah selanjutnya