1. 前言
Amazon ElastiCache 是一种 Web 服务,可让用户在云中轻松设置、管理和扩展分布式内存数据存储或缓存环境。它可以提供高性能、可扩展且具有成本效益的缓存解决方案。同时,它可以帮助消除与部署和管理分布式缓存环境相关的复杂性。
ElastiCache for Redis集群是一个或多个缓存节点的集合,其中所有节点都运行 Redis 缓存引擎软件的实例。ElastiCache for Redis启用集群模式比之禁用集群模式拥有更好的可扩展性尤其是写入可扩展性,更强的高可用性以及更高的资源上限,因而现在越来越多的客户选择ElastiCache for Redis启用集群模式。要使用ElastiCache for Redis集群(启用集群模式),您需要使用可以支持redis集群模式的客户端。
当您的java程序需要连接和使用ElastiCache for redis集群时,通常有三种解决方案可选:jedis,redisson,lettuce,本篇blog将为您介绍如何使用redisson连接和使用ElastiCache for redis集群,除此以外,我们还推出了一系列博客,展示了如何在不同语言中,使用不同的支持ElastiCache集群模式的客户端对ElastiCache集群进行连接和操作,欢迎大家阅读。
2. 搭建测试环境
2.1. 创建测试环境
2.1.1. 创建ElastiCache集群
- 首先创建一个开启TLS及其auth的3 shards,6节点的ElastiCache集群,实例类型为cache.t3.small,版本为5.0.6,按下文的方式获取configuration endpoint,此处假设其值为<Enable TLS and auth ElastiCache configuration endpoint>:<port>
具体获取endpoint的方式如下:
查找 Redis(已启用集群模式)集群的configuration endpoint
- 此时会显示一个列表,其中包含运行任意版本 Redis 的集群。
- 从集群列表中,选择运行“集群 Redis”的集群左侧的复选框。
- 找到 Configuration endpoint。
Redis(已启用集群模式)集群的configuration endpoint
- 其次创建一个关闭TLS且无auth的3 shards,6节点的ElastiCache集群,实例类型为t3.small,版本为5.0.6,按上文的方式获取configuration endpoint,此处假设其值为<No TLS and auth ElastiCache configuration endpoint>:<port>
2.1.2. 创建EC2测试客户端
- 在同一个VPC内创建一个EC2并配置好相应的security group使得EC2上的程序可以访问ElastiCache集群的端口,在EC2上准备好java环境。
mvn -version
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T18:33:14Z)
Maven home: /home/ec2-user/apache-maven-3.5.4
Java version: 11.0.11, vendor: Amazon.com Inc., runtime: /usr/lib/jvm/java-11-amazon-corretto.x86_64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.14.231-173.361.amzn2.x86_64", arch: "amd64", family: "unix"
- 用maven 创建一个 Java project并配置pom.xml
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=myapp -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
cd /home/ec2-user/myapp
vi pom.xml
添加信息如下面红色字样所示:
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>myapp</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>myapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.13.3</version> </dependency>
</dependencies>
<build>
<!-- <sourceDirectory>src</sourceDirectory> -->
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<!-- get all project dependencies -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- MainClass in mainfest make a executable jar -->
<archive>
<manifest>
<mainClass>com.healchow.consumer.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<!-- 配置执行器 -->
<execution>
<id>make-assembly</id>
<!-- 绑定到package命令的生命周期上 -->
<phase>package</phase>
<goals>
<!-- 只运行一次 -->
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2.2. 开启传输中加密(TLS)及auth的ElastiCache集群测试
Amazon ElastiCache 传输中加密是一项可选功能,它允许您在数据最脆弱时候(从一个位置传输到另一个位置时)提高数据的安全性。由于在终端节点加密和解密数据时需要进行一些处理,因此启用传输中加密会对性能产生一些影响。应对使用和不使用传输中加密的数据进行基准测试,以确定对使用案例的性能影响。
2.2.1. ElastiCache 传输中加密可实现以下功能
- 加密连接– 服务器和客户端连接均采用安全套接字层 (SSL) 加密。
- 加密复制– 对在主节点与副本节点之间移动的数据进行加密。
- 服务器身份验证– 客户端可通过身份验证确定它们连接到正确的服务器。
- 客户端身份验证– 使用 Redis AUTH 功能,服务器可以对客户端进行身份验证。
2.2.2. 传输中加密的条件
在规划实现时,应记住有关 Amazon ElastiCache 传输中加密的以下限制:
- 在运行以下 Redis 版本的复制组上支持传输中加密:2.6、4.0.10 和更高版本。
- 只有在 Amazon VPC 中运行的复制组支持传输中加密。
- 只有运行以下节点类型的复制组才支持传输中加密。
- R6g、R5、R4、R3
- M6g、M5、M4、M3
- T4g、T3、T2
有关更多信息,请参阅 受支持的节点类型。
- 通过显式将参数
TransitEncryptionEnabled
设置为 true 可启用传输中加密。
- 只有在创建复制组时才能在复制组中启用传输中加密。无法通过修改复制组来开启和关闭传输中加密。有关在现有复制组中实现传输中加密的信息,请参阅启用传输中加密。
- 要连接到已启用传输中加密的复制组,必须为数据库启用传输层安全性 (TLS)。要连接到未启用传输中加密的复制组,数据库不得启用 TLS。
2.2.3. 加载测试数据
- 使用redis-cli登录开启了TLS和auth的ElastiCache集群并通过cluster nodes命令获取slots在shards中的分布,注意redis-cli要指定tls参数和password,通过redis-cli工具连接集群并进行操作的具体方法请见使用redis-cli连接到 Redis 集群。
redis-cli -c -h <Enable TLS and auth ElastiCache configuration endpoint> --tls -a '<PASSWORD>' -p <port>
cluster nodes
566a91c2f6bdef35595845d8f1ba38b068b30ffe rllca-0001-002---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>@1122 master - 0 1643725098850 19 connected 0-3179 5910-8191
febe51f532cf2caabd9de8cd7bc05173afc2580f rllca-0002-002---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>@1122 master - 0 1643725099857 20 connected 10923-16383
533947912ddb35053ed5dae1bc5f1a83b211d808 rllca-0003-002---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>@1122 slave dee5d61790ae3b202942724a9807d8ac8ea5b9e3 0 1643725100863 21 connected
4b81f4af6dc25eae669ab0ec4bf865472892f11c rllca-0001-001---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>@1122 slave 566a91c2f6bdef35595845d8f1ba38b068b30ffe 0 1643725098000 19 connected
65f162e9c5cb588b0ad3a76a054021bda44ab858 rllca-0002-001---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>@1122 slave febe51f532cf2caabd9de8cd7bc05173afc2580f 0 1643725097842 20 connected
dee5d61790ae3b202942724a9807d8ac8ea5b9e3 rllca-0003-001---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>@1122 myself,master - 0 1643725099000 21 connected 3180-5909 8192-10922
- 分别生成testa,testb,testc三个key,可以通过其slot值结合上文各个shard上slot分布信息,判断出3个key恰好分布在shard1,shard2和shard3上
set testa 1
-> Redirected to slot [2133] located at rllca-0001-002---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>
OK
set testb 1
-> Redirected to slot [14390] located at rllca-0002-002---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:<port>
OK
set testc 1
-> Redirected to slot [10263] located at rllca-0003-001.rllca.n5ffa6.cnn1.cache.aazonaws.com.cn:<port>
OK
2.3.4. 生成java code
- 生成3个
java code redisson_aw1.java
,redisson_aw2.java
和redisson_aw3.java
对ElastiCache分布在三个shard上的testa,testb,testc三个key进行写入,请注意与下面连接不开启TLS及auth的ElastiCache集群相比,地址要以rediss://开头并需要设置密码。同样地请针对不同的java code修改不同的keyname,以redisson_aw1.java
为例。
rm /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_aw1.java
vi /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_aw1.java
package com.mycompany.app;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.redisson.config.Config;
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
public class redisson_aw1 {
public static void main(String[] args) {
Config config = new Config();
config.useClusterServers().setScanInterval(200)// cluster state scan interval in milliseconds
.addNodeAddress("rediss://<Enable TLS and auth ElastiCache configuration endpoint>:<port>")
.setPassword("<PASSWORD>");
RedissonClient redisson = Redisson.create(config);
int count = 0;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
while(count<10000){
try{
RBucket bucket = redisson.getBucket("testa");//此处按照编号读取不同的keyname,比如redisson_aw2对于testb
bucket.set(""+count);
String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
System.out.println(date+" testa number:"+count);//此处按照编号读取不同的keyname,比如redisson_aw2对于testb
Thread.sleep(1000);
count ++ ;
}catch (Exception e) {
e.printStackTrace();
}
}
redisson.shutdown();
}
}
- 生成3个
java code redisson_ar1.java
,redisson_ar2.java
和redisson_ar3.java
对ElastiCache分布在三个shard上的testa,testb,testc三个key进行读取,请注意与下面连接不开启TLS及auth的ElastiCache集群相比,地址要以rediss://开头并需要设置密码。同样地请针对不同的java code修改不同的keyname,以redisson_ar1.java
为例。
rm /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_ar1.java
vi /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_ar1.java
package com.mycompany.app;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.redisson.config.Config;
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
public class redisson_ar1 {
public static void main(String[] args) {
Config config = new Config();
config.useClusterServers().setScanInterval(200)// cluster state scan interval in milliseconds
.addNodeAddress("rediss://clustercfg---rllca---n5ffa6---cnn1---cache.amazonaws.com.rproxy.goskope.com.cn:6379")
.setPassword("AWS1234567891011");
RedissonClient redisson = Redisson.create(config);
int count=0;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
while(count<10000){
try{
RBucket bucket = redisson.getBucket("testa");//此处按照编号读取不同的keyname,比如redisson_aw2对于testb
String objValue = bucket.get().toString();
String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
System.out.println(date+" testa number:"+objValue);//此处按照编号读取不同的keyname,比如redisson_aw2对于testb
Thread.sleep(1000);
count++;
}catch (Exception e) {
e.printStackTrace();
}
}
redisson.shutdown();
}
}
2.3.5. 基本功能测试
- 使用以下命令编译代码并打包:
cd /home/ec2-user/myapp
mvn clean package
- 运行程序并监控ElastiCache开启TLS及Auth集群的CurrConnections , SetTypeCmds以及GetTypeCmds指标,可以发现负载被均衡地发送到了6个节点上,只读副本承担了读取请求,即redisson完成读写分离和负载均衡的工作:
cd /home/ec2-user/myapp/target
nohup java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_aw1 >> aw1.log &
nohup java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_aw2 >> aw2.log &
nohup java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_aw3 >> aw3.log &
nohup java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_ar1 >> ar1.log &
nohup java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_ar2 >> ar2.log &
nohup java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_ar3 >> ar3.log &
具体来说:
- 各个节点上SetTypeCmds数目相同一分钟内都是60,因为只读副本要复制数据,故而只读副本也有SetTypeCmds数目
- 只有只读副本上才有GetTypeCmds,说明redisson进行了读写分离,把只读负载发送到了只读副本
- 从CurrConnections来看,6个节点连接数相同
2.2. 不开启传输中加密(TLS)及auth的ElastiCache集群测试
2.2.1. 加载测试数据
- 使用redis-cli登录开启了TLS和auth的ElastiCache集群并通过cluster nodes命令获取slots在shards中的分布,通过redis-cli工具连接集群并进行操作的具体方法请见使用redis-cli连接到 Redis 集群。
redis-cli -c -h <No TLS and auth ElastiCache configuration endpoint> -p <port>
cluster nodes
1fc915605d94893210fc1d4c04630c2379869e76 10.1.3.34:<port>@1122 slave 8aa247c52f354a98af64bdccae6718efd753b4f3 0 1644055758174 24 connected
8aa247c52f354a98af64bdccae6718efd753b4f3 10.1.1.50:<port>@1122 myself,master - 0 1644055757000 24 connected 10923-16383
d3f03a846448b0ba86e9d7a95dac30c848850bcf 10.1.1.245:<port>@1122 master - 0 1644055757168 26 connected 0-3179 5910-8191
a3daa3b8ab2658c71ffd9b1314b77924c4cafa6a 10.1.1.181:<port>@1122 master - 0 1644055756000 25 connected 3180-5909 8192-10922
2d6f824c1bf2f73cf3ffdb978089038af7f8d302 10.1.3.25:<port>@1122 slave d3f03a846448b0ba86e9d7a95dac30c848850bcf 0 1644055759178 26 connected
c0d98525e5be3513027557c9dc60dc1e9d1f6eb3 10.1.3.104:<port>@1122 slave a3daa3b8ab2658c71ffd9b1314b77924c4cafa6a 0 1644055758000 25 connected
- 分别生成testka,testb,testc三个key,可以通过输出的slot值结合上文各个shard上slot分布信息判断出3个key恰好分布在shard1,shard2和shard3上。
set testa 1
-> Redirected to slot [2133] located at 10.1.1.245:<port>
OK
set testb 1
-> Redirected to slot [14390] located at 10.1.1.50:<port>
OK
set testc 1
-> Redirected to slot [10263] located at 10.1.1.181:<port>
OK
2.2.2. 生成java code
- 生成3个
java code redisson_nar1.java
,redisson_nar2.java
和redisson_nar3.java
对ElastiCache分布在三个shard上的testa,testb,testc三个key进行读取,请注意配置的地址为ElastiCache集群的configuration endpoint,另外请针对不同的java code修改不同的keyname,以redisson_nar1.java
为例。
rm /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_nar1.java
vi /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_nar1.java
package com.mycompany.app;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.redisson.config.Config;
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
public class redisson_nar1 {
public static void main(String[] args) {
Config config = new Config();
config.useClusterServers().setScanInterval(200)// cluster state scan interval in milliseconds
.addNodeAddress("redis://<No TLS and auth ElastiCache configuration endpoint>:<port>");
RedissonClient redisson = Redisson.create(config);
int count=0;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
while(count<10000){
try{
RBucket bucket = redisson.getBucket("testa");//此处按照编号读取不同的keyname,比如redisson_naw2对于testb
String objValue = bucket.get().toString();
String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
System.out.println(date+" testa number:"+objValue);//此处按照编号读取不同的keyname,比如redisson_naw2对于testb
Thread.sleep(1000);
count++;
}catch (Exception e) {
e.printStackTrace();
}
}
redisson.shutdown();
}
}
- 生成3个
java code redisson_naw1.java
,redisson_naw2.java
和redisson_naw3.java
对ElastiCache分布在三个shard上的testa,testb,testc 三个key进行写入,请注意配置的地址为ElastiCache集群的configuration endpoint,另外请针对不同的java code修改不同的keyname,同样以redisson_naw1.java
为例。
rm /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_naw1.java
vi /home/ec2-user/myapp/src/main/java/com/mycompany/app/redisson_naw1.java
package com.mycompany.app;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.redisson.config.Config;
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
public class redisson_naw1 {
public static void main(String[] args) {
Config config = new Config();
config.useClusterServers().setScanInterval(200)// cluster state scan interval in milliseconds
.addNodeAddress("redis://<No TLS and auth ElastiCache configuration endpoint>:<port>");
RedissonClient redisson = Redisson.create(config);
int count = 0;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
while(count<10000){
try{
RBucket bucket = redisson.getBucket("testa");//此处按照编号读取不同的keyname,比如redisson_naw2对于testb
bucket.set(""+count);
String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
System.out.println(date+" testa number:"+count);//此处按照编号读取不同的keyname,比如redisson_naw2对于testb
Thread.sleep(1000);
count ++ ;
}catch (Exception e) {
e.printStackTrace();
}
}
redisson.shutdown();
}
}
2.2.3. 基本功能测试
- 使用以下命令编译代码并打包:
cd /home/ec2-user/myapp
mvn clean package
- 运行程序并监控ElastiCache开启TLS及Auth集群的CurrConnections , SetTypeCmds以及GetTypeCmds指标,可以发现负载被均衡地发送到了6个节点上,只读副本承担了读取请求,即redisson完成读写分离和负载均衡的工作,具体metrics含义和数值与上一章节类似,在此不做赘述。
cd /home/ec2-user/myapp/target
java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_naw1
java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_naw2
java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_naw3
java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_nar1
java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_nar2
java -classpath myapp-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.redisson_nar3
3. failover 测试
因为无论是否开启TLS及Auth,ElastiCache 集群failover的进程是一致的,测试结果也相同。所以我们这里只选取开启TLS及Auth的集群的failover测试过程进行展示,如果有兴趣,各位读者也可以自行测试未开启TLS及Auth的ElastiCache集群的failover过程。
- 首先运行6个java程序,将负载均匀地发送到6个节点上一段时间
- 登录console,对Shard3进行failover,然后观测我们的java程序的输出:
- 我们通过java程序的输出会发现针对shard3的写入会有20秒钟左右的时间无法写入,但针对shard1,shard2的写入以及所有3个shard的读取则一切正常,redisson_aw3在ElastiCache集群failover期间的报错截图如下:
- 从ElastiCache集群的每个节点的CurrConnections指标截图同样可以看出,redisson将读写请求都发送到了shard3新的主节点上,具体说来:
- shard3原来的只读副本现在的主节点rllca-0003-001上SetTypeCmds数目减少,减少的数量和切换时间近似
- 在示例中fail over发生时间09:44及以后的时间,GetTypeCmds数目始终还是60,只不过暂时读写负载都发送到了新的主节点rllca-0003-001上
- 从CurrConnections来看,failover后原来的只读副本现在的主节点rllca-0003-001上的连接数多了一倍,而原来的主节点现在的只读副本rllca-0003-002上没有连接
4. 结束语
本博客为大家展示了如何在java程序中通过redisson连接和操作ElastiCache集群,从这个简单的Demo中我们可以看到redisson能很好地支持ElastiCache集群开启TLS及auth的功能,并自动完成读写分离,负载均衡,failover等工作。通过redisson,我们可以便捷,高效地使用ElastiCache集群。
除了本博客以外,我们还推出了一系列博客,展示了如何在不同语言中使用不同的客户端对ElastiCache集群进行连接和操作,欢迎大家阅读。
相关博客
本篇作者