条条大路通罗马系列—— 使用 Hiredis-cluster 连接 Amazon ElastiCache for Redis 集群

0
0
{"value":"#### **前言**\n\nAmazon ElastiCache for Redis 是速度超快的内存数据存储,能够提供亚毫秒级延迟来支持 实时应用程序。适用于 Redis 的 ElastiCache 基于开源 Redis 构建,可与 Redis API 兼容,能够与 Redis 客户端配合工作,并使用开放的 Redis 数据格式来存储数据。适用于 Redis 的 ElastiCache 兼具开源 Redis 的速度、简单性和多功能性与 Amazon 的可管理性、安全性和可扩展性,能够在游戏、广告技术、电子商务、医疗保健、金融服务和物联网领域支持要求最严苛的实时应用程序。\n\n利用 ElastiCache for Redis,客户可以使用多个分区创建和运行托管的 Redis 集群。当遇到以下三种主要场景时,必须扩展 Redis 环境。第一,如果 Redis 数据的总内存大小超出或预计超出单个虚拟机的内存容量。第二,如果应用程序将数据写入 Redis 的写入吞吐量超出单个虚拟机的容量。第三,如果要将数据分布到多个分区中,以便在单个节点遇到任何问题时,对总体 Redis 环境产生的影响都较小。\n\n我们推出了一系列博客,展示了如何在不同语言中使用不同的支持 ElastiCache 集群模式的客户端对 ElastiCache 集群进行连接和操作,前面已经有一篇博客介绍了 “使用 redission 连接 Amazon ElastiCache for redis 集群”, 今天的主题是讨论如何使用 C 语言连接 Elasticache for redis 集群。通过 redis 官网上查询到现有的客户端,C 语言中支持 redis cluster 的主要有 Hiredis-cluster, hiredis-vip 等,[C++](https://redis.io/docs/clients/#cpp) 语言中支持 redis cluster 的主要有 redis-plus-plus,xredis 等。本篇博客主要会介绍 Hiredis-cluster 的使用。\n\n### **2. 搭建测试环境**\n\n在此说明下,此次测试在宁夏区域进行,测试的主要内容包括:\n\n- 在开启 TLS 及 Auth 的 ElastiCache 集群中验证读写和 Failover\n- 在未开启 TLS 及 Auth 的 ElastiCache 集群中验证读写和 Failover,因为对于未开启 TLS 及 Auth 的集群,Client 端的代码实现会有所不同。\n\n#### **2.1. 创建测试环境**\n\n**创建ElastiCache集群**\n\n1. 首先创建一个开启 TLS 及其 Auth 的3 shards,6节点的ElastiCache 集群,实例类型为 cache.t3.small,版本为5.0.6, 按下文的方式获取 configuration endpoint,此处假设其值为<Enable TLS and auth ElastiCache configuration endpoint>:<port>\n\n具体获取 endpoint 的方式如下:\n\n![image.png](https://dev-media.amazoncloud.cn/8472eea942ca467690a06315a8aa94f2_image.png)\n\n**查找 Redis(已启用集群模式)集群的 configuration endpoint**\n\n- 登录 Amazon Management Console 并打开 ElastiCache 控制台 (https://console.aws.amazon.com/elasticache/)。\n- 从导航窗格中,选择 Redis。\n- 此时会显示一个列表,其中包含运行任意版本 Redis 的集群。\n- \n- 从集群列表中,选择运行“集群 Redis”的集群左侧的复选框。\n- 此时屏幕会展开,以显示有关选定集群的详细信息。\n- \n- 找到 Configuration endpoint\n\n![image.png](https://dev-media.amazoncloud.cn/dc30bab664ea4094b12bc07020f4dbc4_image.png)\n\n2.创建一个关闭 TLS 且无 auth 的3 shards,6节点的 ElastiCache 集群,实例类型为 cache.t3.medium,版本为5.0.6,按上文的方式获取 configuration endpoint,此处假设其值为<No TLS and auth ElastiCache configuration endpoint>:<port>\n\n![image.png](https://dev-media.amazoncloud.cn/883f1f4bcef14b38951919baff2427cd_image.png)\n\n**创建EC2测试客户端**\n\n1.在同一个 VPC 内创建一个 EC2并配置好响应 security group,我使用的 EC2是 Ubuntu 18.04,安装的环境是一些 Ubuntu 的参考命令。\n\n2.在 EC2上准备环境, Hiredis-cluster 是基于 hiredis 扩展编写的, 我们环境准备准备的时候也会涉及到 hiredis 部分,\n\n- C Compiler 安装,这里我使用 GCC,可参考这个 How to install GCC Compiler on Ubuntu 18.04\n- libssl-dev 安装,sudo apt-get install libssl-dev 注意在CMake 之前安装,否则会报错 “Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_CRYPTO_LIBRARY OPENSSL_INCLUDE_DIR)”\n- CMake 安装,我使用的是 CMake 编译,CMake 官网提供了安装方式https://cmake.org/install/\n- libevent-dev 安装,sudo apt install libevent-dev, 直接安装可能会报如下错误,解决办法是\n\n```\nCMake Error at /usr/local/share/cmake-3.23/Modules/FindPackageHandleStandardArgs.cmake:230 (message):\n Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)\nCall Stack (most recent call first):\n /usr/local/share/cmake-3.23/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)\n /usr/local/share/cmake-3.23/Modules/FindPkgConfig.cmake:99 (find_package_handle_standard_args)\n tests/CMakeLists.txt:40 (find_package)\n\n```\n\n- hiredis 下载并编译,因为 hiredis-cluster 是基于 hiredis 扩展编写的,所以需要安装并编译 hiredis。 如下命令,因为会用到 SSL,所以我使用 make USE_SSL=1, 可以直接使用 make 替代。\n\n```\ngit clone https://github.com/redis/hiredis.git\ncd hiredis\nmake USE_SSL=1\nsudo make install\n```\n\n- hiredis-cluster 环境准备,因为会测试 ssl,所以通过选项指定,Redis\n\n```\nmkdir build; cd build\ncmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_SSL=ON ..\nmake\n```\n\n3.在上面的步骤完成后,你的环境会是这样的:\n\nhiredis 相关,<path to hiredis>中会生成 hiredis_ssl lib 文件,在</usr/local/lib/>中 hiredis.a 文件\n\n![image.png](https://dev-media.amazoncloud.cn/d1e21c86f71448d5a022ddd0e64b9a4e_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/58235bfa35bd473ba4cce9d9eb48cd81_image.png)\n\n<path to hiredis-cluster> 中,hiredis_cluster lib 文件生成\n\n![image.png](https://dev-media.amazoncloud.cn/a7a989aa0f334024bab4c03c9906621e_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/d9ffd5616386406187aad92d95bd7140_image.png)\n\n\n#### **2.2 不开启传输中加密(TLS)及 auth 的 ElastiCache 集群测试**\n\n**加载测试数据**\n\n1. 使用 redis-cli 登录未开启了 TLS 和 auth 的 ElastiCache 集群并通过 cluster nodes 命令获取 slots 在 shards 中的分布,通过 redis-cli 工具连接集群并进行操作的具体方法请见[使用 redis-cli 连接到 Redis 集群](https://docs.aws.amazon.com/zh_cn/AmazonElastiCache/latest/red-ug/nodes-connecting.html)。\n\nredis-cli -c -h <No TLS and auth ElastiCache configuration endpoint> -p <port>\n\n![image.png](https://dev-media.amazoncloud.cn/68d374b9539b426995185fdce13614ba_image.png)\n \n2. 分别生成 testka,testb,testc 三个 key,可以通过其 slot 值结合上文各个 shard 上 slot 分布信息,判断出3个 key 恰好分布在 shard1,shard2 和 shard3 上\n\n![image.png](https://dev-media.amazoncloud.cn/db2deafe92a847b08feba189940e96d2_image.png)\n\n**C代码**\n\n1.以下是写入 ElastiCache 集群的代码, 针对于 Elasticache 三个 shard 上的 test1,test2,test3 三个 key 进行写入,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,另外修改不同的 keyname。\n\n```\n#include \"hircluster.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n struct timeval timeout = {1, 500000}; // 1.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, \"<No TLS and auth ElastiCache configuration endpoint>:<port>\"); \n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterConnect2(cc);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count < 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, \"SET %s %d\", \"test1\", count);\n printf(\"SET: %d\\n\", count);\n count++ ;\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n }\n freeReplyObject(reply);\n sleep(1);\n }\n\n\n redisClusterFree(cc);\n return 0;\n}\n```\n\n2.以下是读取 ElastiCache 集群的代码, 针对于 Elasticache 三个 shard 上的 test1,test2,test3 三个 key 进行读取,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,另外修改不同的 keyname。\n\n```\n#include \"hircluster.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n struct timeval timeout = {1, 500000}; // 1.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, \"<No TLS and auth ElastiCache configuration endpoint>:<port>\"); \n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterConnect2(cc);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count < 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, \"GET %s\", \"test1\");\n if(reply && reply->str) {\n printf(\"GET: %s\\n\", reply->str);\n \n }\n freeReplyObject(reply);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n }\n count++;\n sleep(1);\n }\n\n redisClusterFree(cc);\n return 0;\n}\n```\n\n**基本功能测试**\n\n1.使用如下命令编译代码:\n\n```\ngcc hiredisc-naw1.c -o hiredisc-naw1 -L./build -lhiredis_cluster /usr/local/lib/libhiredis.a -L~/hiredis -lhiredis_ssl\n```\n\n2.运行程序并监控 ElastiCache 未开启 TLS 及 Auth 集群的 CurrConnections 指标,可以看到负载被均衡地发送到了6个节点上,即 hiredis-cluster 可以完成读写分离和负载均衡的工作:\n\n我的代码在~/hiredis-cluster 路径下面,所以\n\n```\ncd hiredis-cluster/ &&./hiredisc-naw1\ncd hiredis-cluster/ &&./hiredisc-naw2\ncd hiredis-cluster/ &&./hiredisc-naw3\ncd hiredis-cluster/ &&./hiredisc-nar1\ncd hiredis-cluster/ &&./hiredisc-nar2\ncd hiredis-cluster/ &&./hiredisc-nar3\n```\n\n![image.png](https://dev-media.amazoncloud.cn/4ec7f563fe8d4f8c8f823b58150fd2ed_image.png)\n\n#### **2.3. 开启传输中加密(TLS)及 auth 的 ElastiCache 集群测试**\n\nAmazon ElastiCache 传输中加密是一项可选功能,它允许您在数据最脆弱时候(从一个位置传输到另一个位置时)提高数据的安全性。由于在终端节点加密和解密数据时需要进行一些处理,因此启用传输中加密会对性能产生一些影响。应对使用和不使用传输中加密的数据进行基准测试,以确定对使用案例的性能影响。 关于 TLS 加密这一块,具体可以参考条条大路通罗马- 使用 redission 连接 Amazon ElastiCache for redis 集群。\n\n**加载测试数据**\n\n1.使用 redis-cli 登录开启了 TLS 和 auth 的 ElastiCache 集群并通过 cluster nodes 命令获取 slots 在 shards 中的分布,通过 redis-cli 工具连接集群并进行操作的具体方法请见使用 redis-cli 连接到 Redis 集群。\n\n![111.png](https://dev-media.amazoncloud.cn/8b4499e2506b4d14a16611d9c4e3421f_111.png)\n\nredis-cli -c -h <No TLS and auth ElastiCache configuration endpoint> -p <port>\n\n2.分别生成 testka,testb,testc 三个 key,可以通过其 slot 值结合上文各个 shard 上 slot 分布信息,判断出 3 个 key 恰好分布在 shard1,shard2 和 shard3 上\n\n![222.png](https://dev-media.amazoncloud.cn/90d7646fa170460f9d83422341b7ab00_222.png)\n\n**C代码**\n\n1.以下是写入 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行写入,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,设置密码,另外修改不同的 keyname。这里需要注意的是 hiredis-cluster 对 SSL 的支持,可以分为两种方式,一种是指定 SSL 证书路径,另一种是指定证书,对于 EC2 访问 ElastiCache 的情况,指定证书路径即可, 具体可参考 hiredis [的文档]()。\n\n```\n#include \"hiredis_cluster/hircluster.h\"\n\n#include \"hiredis/hiredis_ssl.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n#define CLUSTER_NODE_TLS \":<Enable TLS and auth ElastiCache configuration endpoint>:<port>\"\n#define CLUSTER_PASSWORD \"<PASSWORD>\"\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n\n redisSSLContext *ssl;\n redisSSLContextError ssl_error;\n\n redisInitOpenSSL();\n ssl = redisCreateSSLContext(NULL, \"/etc/ssl/certs\", NULL, NULL,\n NULL, &ssl_error);\n if (!ssl) {\n printf(\"SSL Context error: %s\\n\", redisSSLContextGetError(ssl_error));\n exit(1);\n }\n\n struct timeval timeout = {5, 500000}; // 5.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS);\n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterSetOptionEnableSSL(cc, ssl);\n redisClusterSetOptionPassword(cc, CLUSTER_PASSWORD);\n redisClusterConnect2(cc);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count < 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, \"SET %s %d\", \"test1\", count);\n printf(\"SET: %d\\n\", count);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n }\n count++ ;\n freeReplyObject(reply);\n sleep(1);\n }\n\n redisClusterFree(cc);\n redisFreeSSLContext(ssl);\n return 0;\n}\n```\n\n2.以下是读取 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行读取,请注意配置的地址为 ElastiCache 集群的 configuration endpoint ,另外修改不同的 keyname。\n\n```\n#include \"hircluster.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n struct timeval timeout = {1, 500000}; // 1.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, \"<No TLS and auth ElastiCache configuration endpoint>:<port>\"); \n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterConnect2(cc);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count < 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, \"GET %s\", \"test1\");\n if(reply && reply->str) {\n printf(\"GET: %s\\n\", reply->str);\n \n }\n freeReplyObject(reply);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n }\n count++;\n sleep(1);\n }\n\n redisClusterFree(cc);\n return 0;\n}\n```\n\n**基本功能测试**\n\n1.使用如下命令编译代码:\n\n```gcc hiredisc-naw1.c -o hiredisc-naw1 -L./build -lhiredis_cluster /usr/local/lib/libhiredis.a -L~/hiredis -lhiredis_ssl```\n\n2.运行程序并监控 ElastiCache 未开启 TLS 及 Auth 集群的CurrConnections 指标,可以看到负载被均衡地发送到了6个节点上,即 hiredis-cluster 可以完成读写分离和负载均衡的工作:\n\n我的代码在~/hiredis-cluster 路径下面,所以\n\n```\ncd hiredis-cluster/ &&./hiredisc-naw1\ncd hiredis-cluster/ &&./hiredisc-naw2\ncd hiredis-cluster/ &&./hiredisc-naw3\ncd hiredis-cluster/ &&./hiredisc-nar1\ncd hiredis-cluster/ &&./hiredisc-nar2\ncd hiredis-cluster/ &&./hiredisc-nar3\n```\n\n![image.png](https://dev-media.amazoncloud.cn/f7983fcb5f8b4886bb8c4df046239a89_image.png)\n\n#### **2.3. 开启传输中加密(TLS)及 auth 的 ElastiCache 集群测试**\n\nAmazon ElastiCache 传输中加密是一项可选功能,它允许您在数据最脆弱时候(从一个位置传输到另一个位置时)提高数据的安全性。由于在终端节点加密和解密数据时需要进行一些处理,因此启用传输中加密会对性能产生一些影响。应对使用和不使用传输中加密的数据进行基准测试,以确定对使用案例的性能影响。 关于 TLS 加密这一块,具体可以参考条条大路通罗马- 使用 redission 连接 Amazon ElastiCache for redis 集群。\n\n**加载测试数据**\n\n1.使用 redis-cli 登录开启了 TLS 和 auth 的 ElastiCache 集群并通过 cluster nodes 命令获取 slots 在 shards 中的分布,通过 redis-cli 工具连接集群并进行操作的具体方法请见使用 redis-cli 连接到 Redis 集群。\n\n![image.png](https://dev-media.amazoncloud.cn/b1e1e21bd835462babe59759516d6fff_image.png)\n\nredis-cli -c -h <No TLS and auth ElastiCache configuration endpoint> -p <port>\n\n2.分别生成 testka,testb,testc 三个 key,可以通过其 slot 值结合上文各个 shard 上 slot 分布信息,判断出 3 个 key 恰好分布在shard1,shard2 和 shard3 上\n\n![image.png](https://dev-media.amazoncloud.cn/973b537d1e7c47778976bd56277fc57a_image.png)\n\n**C代码**\n\n1.以下是写入 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行写入,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,设置密码,另外修改不同的 keyname。这里需要注意的是 hiredis-cluster 对SSL的支持,可以分为两种方式,一种是指定 SSL 证书路径,另一种是指定证书,对于 EC2 访问 ElastiCache 的情况,指定证书路径即可, 具体可参考 hiredis 的文档。\n\n```\n#include \"hiredis_cluster/hircluster.h\"\n\n#include \"hiredis/hiredis_ssl.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n#define CLUSTER_NODE_TLS \":<Enable TLS and auth ElastiCache configuration endpoint>:<port>\"\n#define CLUSTER_PASSWORD \"<PASSWORD>\"\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n\n redisSSLContext *ssl;\n redisSSLContextError ssl_error;\n\n redisInitOpenSSL();\n ssl = redisCreateSSLContext(NULL, \"/etc/ssl/certs\", NULL, NULL,\n NULL, &ssl_error);\n if (!ssl) {\n printf(\"SSL Context error: %s\\n\", redisSSLContextGetError(ssl_error));\n exit(1);\n }\n\n struct timeval timeout = {5, 500000}; // 5.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS);\n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterSetOptionEnableSSL(cc, ssl);\n redisClusterSetOptionPassword(cc, CLUSTER_PASSWORD);\n redisClusterConnect2(cc);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count < 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, \"SET %s %d\", \"test1\", count);\n printf(\"SET: %d\\n\", count);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n }\n count++ ;\n freeReplyObject(reply);\n sleep(1);\n }\n\n redisClusterFree(cc);\n redisFreeSSLContext(ssl);\n return 0;\n}\n```\n\n2.以下是读取 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行读取,请注意配置的地址为 ElastiCache 集群的 configuration endpoint ,另外修改不同的 keyname 。\n\n```\n#include \"hiredis_cluster/hircluster.h\"\n\n#include \"hiredis/hiredis_ssl.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n#define CLUSTER_NODE_TLS \"<Enable TLS and auth ElastiCache configuration endpoint>:<port>\"\n#define CLUSTER_PASSWORD \"<PASSWORD>\"\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n\n redisSSLContext *ssl;\n redisSSLContextError ssl_error;\n\n redisInitOpenSSL();\n ssl = redisCreateSSLContext(NULL, \"/etc/ssl/certs\", NULL, NULL,\n NULL, &ssl_error);\n if (!ssl) {\n printf(\"SSL Context error: %s\\n\", redisSSLContextGetError(ssl_error));\n exit(1);\n }\n\n struct timeval timeout = {5, 500000}; // 5.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS);\n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterSetOptionEnableSSL(cc, ssl);\n redisClusterSetOptionPassword(cc, CLUSTER_PASSWORD);\n redisClusterConnect2(cc);\n if (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count < 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, \"GET %s\", \"test1\");\n if(reply && reply->str) {\n printf(\"GET: %s\\n\", reply->str);\n \n }\n freeReplyObject(reply);\nif (cc && cc->err) {\n printf(\"Error: %s\\n\", cc->errstr);\n }\n count++;\n sleep(1);\n }\n\n redisClusterFree(cc);\n redisFreeSSLContext(ssl);\n return 0;\n}\n```\n\n**基本功能测试**\n\n1.使用如下命令编译代码:\n\n```gcc hiredisc-aw1.c -o hiredisc-aw1 -L./build -lhiredis_cluster /usr/local/lib/libhiredis.a -L~/hiredis -lhiredis_ssl```\n\n2.运行程序并监控 ElastiCache 开启 TLS 及 Auth 集群的CurrConnections 指标,可以看到负载被均衡地发送到了 6 个节点上,即 hiredis-cluster 可以完成读写分离和负载均衡的工作:\n\n我的代码在~/hiredis-cluster 路径下面,所以\n\n```\ncd hiredis-cluster/ &&./hiredisc-aw1\ncd hiredis-cluster/ &&./hiredisc-aw2\ncd hiredis-cluster/ &&./hiredisc-aw3\ncd hiredis-cluster/ &&./hiredisc-ar1\ncd hiredis-cluster/ &&./hiredisc-ar2\ncd hiredis-cluster/ &&./hiredisc-ar3\n```\n\n![image.png](https://dev-media.amazoncloud.cn/ea346774103842b5b705aac8be576157_image.png)\n\n### **三级标题**\n\n#### **3.1. 未开启 TLS 及 auth 的 ElastiCache failover 测试**\n\n登录 console,对 Shard1 进行 failover,然后观测我们的C程序的输出, 此过程大约持续12s ((观测所得)),\n\n![image.png](https://dev-media.amazoncloud.cn/a5386fa2fe29449da320a5dc499dce16_image.png)\n\n从 ElastiCache 集群的每个节点的 CurrConnections 指标截图同样可以看出,hiredis-cluster 将读写请求都发送到了 shard1 新的主节点上:\n\n![image.png](https://dev-media.amazoncloud.cn/06f58f2d51b94171bd98a251c3eeb975_image.png)\n\n#### **3.2. 开启 TLS 及 auth 的 ElastiCache failover 测试**\n\n登录 console,对 Shard1 进行 failover,然后观测我们的C程序的输出,这个时间大概 10s 左右(观测所得),\n\n![image.png](https://dev-media.amazoncloud.cn/576c49d8eab943319ba388e67c85e9b6_image.png)\n\n从 ElastiCache 集群的每个节点的 CurrConnections 指标截图同样可以看出, hiredis-cluster 将读写请求都发送到了 shard1 新的主节点上:\n\n![image.png](https://dev-media.amazoncloud.cn/7fd624e9c6834143a0041be1cc5c6c99_image.png)\n\n**failover 测试中发生问题以及解决方案**\n\n在做 failover 的时候发现一个问题,在 failover 过程中,有的时候client 端直接断开链接,有的时候成功,后来经过分析 hiredis 的代码得知需要把 timeout 时间调整下,所以从 1.5s 调整为 5.5s, 注意这个值并不是最佳实践,是根据测试得来的。\n\n### **4. 结束语**\n\n在本博客中,主要使用C语言通过 hiredis-cluster 连接和操作ElastiCache 集群,从简单的 Demo 中我们可以看到 hiredis-cluster 可以很好地支持 ElastiCache 集群开启 TLS 及 auth 等功能,并自动完成读写分离,负载均衡,failover 等工作,有助于我们便捷,高效地使用 ElastiCache。\n\n### **5.参考**\n\nHiredis-cluster 文档:[https://github.com/Nordix/hiredis-cluster](https://github.com/Nordix/hiredis-cluster)\n\nHiredis 文档: [https://github.com/redis/hiredis](https://github.com/redis/hiredis)\n\nAmazon Elasticache : [https://aws.amazon.com/elasticache/redis/](https://aws.amazon.com/elasticache/redis/)\n\n条条大路通罗马 —— 使用 redisson 连接 Amazon ElastiCache for redis 集群:[https://aws.amazon.com/cn/blogs/china/connecting-amazon-elasticache-for-redis-cluster-using-redisson/](https://aws.amazon.com/cn/blogs/china/connecting-amazon-elasticache-for-redis-cluster-using-redisson/)\n\n### **相关博客**\n[条条大路通罗马 —— 使用 redisson 连接 Amazon ElastiCache for redis 集群](https://aws.amazon.com/cn/blogs/china/connecting-amazon-elasticache-for-redis-cluster-using-redisson/)\n\n[条条大路通罗马 —— 使用 redis-py 访问 Amazon ElastiCache for redis集群](https://aws.amazon.com/cn/blogs/china/use-redis-py-to-access-amazon-elasticache-for-redis-cluster/)\n\n[条条大路通罗马 —— 使用 go-redis 连接 Amazon ElastiCache for Redis 集群](https://aws.amazon.com/cn/blogs/china/all-roads-lead-to-rome-use-go-redis-to-connect-amazon-elasticache-for-redis-cluster/)\n\n### **本篇作者**\n\n![image.png](https://dev-media.amazoncloud.cn/ab27d6fa467b473aa8e689ab6eaad5ea_image.png)\n\n#### **冯秋爽**\n\nAmazon 解决方案架构师,负责跨国企业级客户基于 Amazon 的技术架构设计、咨询和设计优化工作。在加入 Amazon 之前曾就职于 IBM 、甲骨文等 IT 企业,积累了丰富的程序开发和数据库的实践经验。\n\n\n\n\n\n\n\n","render":"<h4><a id=\"_0\"></a><strong>前言</strong></h4>\n<p>Amazon ElastiCache for Redis 是速度超快的内存数据存储,能够提供亚毫秒级延迟来支持 实时应用程序。适用于 Redis 的 ElastiCache 基于开源 Redis 构建,可与 Redis API 兼容,能够与 Redis 客户端配合工作,并使用开放的 Redis 数据格式来存储数据。适用于 Redis 的 ElastiCache 兼具开源 Redis 的速度、简单性和多功能性与 Amazon 的可管理性、安全性和可扩展性,能够在游戏、广告技术、电子商务、医疗保健、金融服务和物联网领域支持要求最严苛的实时应用程序。</p>\n<p>利用 ElastiCache for Redis,客户可以使用多个分区创建和运行托管的 Redis 集群。当遇到以下三种主要场景时,必须扩展 Redis 环境。第一,如果 Redis 数据的总内存大小超出或预计超出单个虚拟机的内存容量。第二,如果应用程序将数据写入 Redis 的写入吞吐量超出单个虚拟机的容量。第三,如果要将数据分布到多个分区中,以便在单个节点遇到任何问题时,对总体 Redis 环境产生的影响都较小。</p>\n<p>我们推出了一系列博客,展示了如何在不同语言中使用不同的支持 ElastiCache 集群模式的客户端对 ElastiCache 集群进行连接和操作,前面已经有一篇博客介绍了 “使用 redission 连接 Amazon ElastiCache for redis 集群”, 今天的主题是讨论如何使用 C 语言连接 Elasticache for redis 集群。通过 redis 官网上查询到现有的客户端,C 语言中支持 redis cluster 的主要有 Hiredis-cluster, hiredis-vip 等,<a href=\"https://redis.io/docs/clients/#cpp\" target=\"_blank\">C++</a> 语言中支持 redis cluster 的主要有 redis-plus-plus,xredis 等。本篇博客主要会介绍 Hiredis-cluster 的使用。</p>\n<h3><a id=\"2__8\"></a><strong>2. 搭建测试环境</strong></h3>\n<p>在此说明下,此次测试在宁夏区域进行,测试的主要内容包括:</p>\n<ul>\n<li>在开启 TLS 及 Auth 的 ElastiCache 集群中验证读写和 Failover</li>\n<li>在未开启 TLS 及 Auth 的 ElastiCache 集群中验证读写和 Failover,因为对于未开启 TLS 及 Auth 的集群,Client 端的代码实现会有所不同。</li>\n</ul>\n<h4><a id=\"21__15\"></a><strong>2.1. 创建测试环境</strong></h4>\n<p><strong>创建ElastiCache集群</strong></p>\n<ol>\n<li>首先创建一个开启 TLS 及其 Auth 的3 shards,6节点的ElastiCache 集群,实例类型为 cache.t3.small,版本为5.0.6, 按下文的方式获取 configuration endpoint,此处假设其值为&lt;Enable TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;</li>\n</ol>\n<p>具体获取 endpoint 的方式如下:</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/8472eea942ca467690a06315a8aa94f2_image.png\" alt=\"image.png\" /></p>\n<p><strong>查找 Redis(已启用集群模式)集群的 configuration endpoint</strong></p>\n<ul>\n<li>登录 Amazon Management Console 并打开 ElastiCache 控制台 (https://console.aws.amazon.com/elasticache/)。</li>\n<li>从导航窗格中,选择 Redis。</li>\n<li>此时会显示一个列表,其中包含运行任意版本 Redis 的集群。</li>\n<li></li>\n<li>从集群列表中,选择运行“集群 Redis”的集群左侧的复选框。</li>\n<li>此时屏幕会展开,以显示有关选定集群的详细信息。</li>\n<li></li>\n<li>找到 Configuration endpoint</li>\n</ul>\n<p><img src=\"https://dev-media.amazoncloud.cn/dc30bab664ea4094b12bc07020f4dbc4_image.png\" alt=\"image.png\" /></p>\n<p>2.创建一个关闭 TLS 且无 auth 的3 shards,6节点的 ElastiCache 集群,实例类型为 cache.t3.medium,版本为5.0.6,按上文的方式获取 configuration endpoint,此处假设其值为&lt;No TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/883f1f4bcef14b38951919baff2427cd_image.png\" alt=\"image.png\" /></p>\n<p><strong>创建EC2测试客户端</strong></p>\n<p>1.在同一个 VPC 内创建一个 EC2并配置好响应 security group,我使用的 EC2是 Ubuntu 18.04,安装的环境是一些 Ubuntu 的参考命令。</p>\n<p>2.在 EC2上准备环境, Hiredis-cluster 是基于 hiredis 扩展编写的, 我们环境准备准备的时候也会涉及到 hiredis 部分,</p>\n<ul>\n<li>C Compiler 安装,这里我使用 GCC,可参考这个 How to install GCC Compiler on Ubuntu 18.04</li>\n<li>libssl-dev 安装,sudo apt-get install libssl-dev 注意在CMake 之前安装,否则会报错 “Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_CRYPTO_LIBRARY OPENSSL_INCLUDE_DIR)”</li>\n<li>CMake 安装,我使用的是 CMake 编译,CMake 官网提供了安装方式https://cmake.org/install/</li>\n<li>libevent-dev 安装,sudo apt install libevent-dev, 直接安装可能会报如下错误,解决办法是</li>\n</ul>\n<pre><code class=\"lang-\">CMake Error at /usr/local/share/cmake-3.23/Modules/FindPackageHandleStandardArgs.cmake:230 (message):\n Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)\nCall Stack (most recent call first):\n /usr/local/share/cmake-3.23/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)\n /usr/local/share/cmake-3.23/Modules/FindPkgConfig.cmake:99 (find_package_handle_standard_args)\n tests/CMakeLists.txt:40 (find_package)\n\n</code></pre>\n<ul>\n<li>hiredis 下载并编译,因为 hiredis-cluster 是基于 hiredis 扩展编写的,所以需要安装并编译 hiredis。 如下命令,因为会用到 SSL,所以我使用 make USE_SSL=1, 可以直接使用 make 替代。</li>\n</ul>\n<pre><code class=\"lang-\">git clone https://github.com/redis/hiredis.git\ncd hiredis\nmake USE_SSL=1\nsudo make install\n</code></pre>\n<ul>\n<li>hiredis-cluster 环境准备,因为会测试 ssl,所以通过选项指定,Redis</li>\n</ul>\n<pre><code class=\"lang-\">mkdir build; cd build\ncmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_SSL=ON ..\nmake\n</code></pre>\n<p>3.在上面的步骤完成后,你的环境会是这样的:</p>\n<p>hiredis 相关,&lt;path to hiredis&gt;中会生成 hiredis_ssl lib 文件,在&lt;/usr/local/lib/&gt;中 hiredis.a 文件</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/d1e21c86f71448d5a022ddd0e64b9a4e_image.png\" alt=\"image.png\" /></p>\n<p><img src=\"https://dev-media.amazoncloud.cn/58235bfa35bd473ba4cce9d9eb48cd81_image.png\" alt=\"image.png\" /></p>\n<p>&lt;path to hiredis-cluster&gt; 中,hiredis_cluster lib 文件生成</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/a7a989aa0f334024bab4c03c9906621e_image.png\" alt=\"image.png\" /></p>\n<p><img src=\"https://dev-media.amazoncloud.cn/d9ffd5616386406187aad92d95bd7140_image.png\" alt=\"image.png\" /></p>\n<h4><a id=\"22_TLS_auth__ElastiCache__95\"></a><strong>2.2 不开启传输中加密(TLS)及 auth 的 ElastiCache 集群测试</strong></h4>\n<p><strong>加载测试数据</strong></p>\n<ol>\n<li>使用 redis-cli 登录未开启了 TLS 和 auth 的 ElastiCache 集群并通过 cluster nodes 命令获取 slots 在 shards 中的分布,通过 redis-cli 工具连接集群并进行操作的具体方法请见<a href=\"https://docs.aws.amazon.com/zh_cn/AmazonElastiCache/latest/red-ug/nodes-connecting.html\" target=\"_blank\">使用 redis-cli 连接到 Redis 集群</a>。</li>\n</ol>\n<p>redis-cli -c -h &lt;No TLS and auth ElastiCache configuration endpoint&gt; -p &lt;port&gt;</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/68d374b9539b426995185fdce13614ba_image.png\" alt=\"image.png\" /></p>\n<ol start=\"2\">\n<li>分别生成 testka,testb,testc 三个 key,可以通过其 slot 值结合上文各个 shard 上 slot 分布信息,判断出3个 key 恰好分布在 shard1,shard2 和 shard3 上</li>\n</ol>\n<p><img src=\"https://dev-media.amazoncloud.cn/db2deafe92a847b08feba189940e96d2_image.png\" alt=\"image.png\" /></p>\n<p><strong>C代码</strong></p>\n<p>1.以下是写入 ElastiCache 集群的代码, 针对于 Elasticache 三个 shard 上的 test1,test2,test3 三个 key 进行写入,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,另外修改不同的 keyname。</p>\n<pre><code class=\"lang-\">#include &quot;hircluster.h&quot;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n struct timeval timeout = {1, 500000}; // 1.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, &quot;&lt;No TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;&quot;); \n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterConnect2(cc);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count &lt; 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, &quot;SET %s %d&quot;, &quot;test1&quot;, count);\n printf(&quot;SET: %d\\n&quot;, count);\n count++ ;\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n }\n freeReplyObject(reply);\n sleep(1);\n }\n\n\n redisClusterFree(cc);\n return 0;\n}\n</code></pre>\n<p>2.以下是读取 ElastiCache 集群的代码, 针对于 Elasticache 三个 shard 上的 test1,test2,test3 三个 key 进行读取,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,另外修改不同的 keyname。</p>\n<pre><code class=\"lang-\">#include &quot;hircluster.h&quot;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n struct timeval timeout = {1, 500000}; // 1.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, &quot;&lt;No TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;&quot;); \n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterConnect2(cc);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count &lt; 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, &quot;GET %s&quot;, &quot;test1&quot;);\n if(reply &amp;&amp; reply-&gt;str) {\n printf(&quot;GET: %s\\n&quot;, reply-&gt;str);\n \n }\n freeReplyObject(reply);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n }\n count++;\n sleep(1);\n }\n\n redisClusterFree(cc);\n return 0;\n}\n</code></pre>\n<p><strong>基本功能测试</strong></p>\n<p>1.使用如下命令编译代码:</p>\n<pre><code class=\"lang-\">gcc hiredisc-naw1.c -o hiredisc-naw1 -L./build -lhiredis_cluster /usr/local/lib/libhiredis.a -L~/hiredis -lhiredis_ssl\n</code></pre>\n<p>2.运行程序并监控 ElastiCache 未开启 TLS 及 Auth 集群的 CurrConnections 指标,可以看到负载被均衡地发送到了6个节点上,即 hiredis-cluster 可以完成读写分离和负载均衡的工作:</p>\n<p>我的代码在~/hiredis-cluster 路径下面,所以</p>\n<pre><code class=\"lang-\">cd hiredis-cluster/ &amp;&amp;./hiredisc-naw1\ncd hiredis-cluster/ &amp;&amp;./hiredisc-naw2\ncd hiredis-cluster/ &amp;&amp;./hiredisc-naw3\ncd hiredis-cluster/ &amp;&amp;./hiredisc-nar1\ncd hiredis-cluster/ &amp;&amp;./hiredisc-nar2\ncd hiredis-cluster/ &amp;&amp;./hiredisc-nar3\n</code></pre>\n<p><img src=\"https://dev-media.amazoncloud.cn/4ec7f563fe8d4f8c8f823b58150fd2ed_image.png\" alt=\"image.png\" /></p>\n<h4><a id=\"23_TLS_auth__ElastiCache__220\"></a><strong>2.3. 开启传输中加密(TLS)及 auth 的 ElastiCache 集群测试</strong></h4>\n<p>Amazon ElastiCache 传输中加密是一项可选功能,它允许您在数据最脆弱时候(从一个位置传输到另一个位置时)提高数据的安全性。由于在终端节点加密和解密数据时需要进行一些处理,因此启用传输中加密会对性能产生一些影响。应对使用和不使用传输中加密的数据进行基准测试,以确定对使用案例的性能影响。 关于 TLS 加密这一块,具体可以参考条条大路通罗马- 使用 redission 连接 Amazon ElastiCache for redis 集群。</p>\n<p><strong>加载测试数据</strong></p>\n<p>1.使用 redis-cli 登录开启了 TLS 和 auth 的 ElastiCache 集群并通过 cluster nodes 命令获取 slots 在 shards 中的分布,通过 redis-cli 工具连接集群并进行操作的具体方法请见使用 redis-cli 连接到 Redis 集群。</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/8b4499e2506b4d14a16611d9c4e3421f_111.png\" alt=\"111.png\" /></p>\n<p>redis-cli -c -h &lt;No TLS and auth ElastiCache configuration endpoint&gt; -p &lt;port&gt;</p>\n<p>2.分别生成 testka,testb,testc 三个 key,可以通过其 slot 值结合上文各个 shard 上 slot 分布信息,判断出 3 个 key 恰好分布在 shard1,shard2 和 shard3 上</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/90d7646fa170460f9d83422341b7ab00_222.png\" alt=\"222.png\" /></p>\n<p><strong>C代码</strong></p>\n<p>1.以下是写入 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行写入,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,设置密码,另外修改不同的 keyname。这里需要注意的是 hiredis-cluster 对 SSL 的支持,可以分为两种方式,一种是指定 SSL 证书路径,另一种是指定证书,对于 EC2 访问 ElastiCache 的情况,指定证书路径即可, 具体可参考 hiredis <a href=\"\" target=\"_blank\">的文档</a>。</p>\n<pre><code class=\"lang-\">#include &quot;hiredis_cluster/hircluster.h&quot;\n\n#include &quot;hiredis/hiredis_ssl.h&quot;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\n#define CLUSTER_NODE_TLS &quot;:&lt;Enable TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;&quot;\n#define CLUSTER_PASSWORD &quot;&lt;PASSWORD&gt;&quot;\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n\n redisSSLContext *ssl;\n redisSSLContextError ssl_error;\n\n redisInitOpenSSL();\n ssl = redisCreateSSLContext(NULL, &quot;/etc/ssl/certs&quot;, NULL, NULL,\n NULL, &amp;ssl_error);\n if (!ssl) {\n printf(&quot;SSL Context error: %s\\n&quot;, redisSSLContextGetError(ssl_error));\n exit(1);\n }\n\n struct timeval timeout = {5, 500000}; // 5.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS);\n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterSetOptionEnableSSL(cc, ssl);\n redisClusterSetOptionPassword(cc, CLUSTER_PASSWORD);\n redisClusterConnect2(cc);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count &lt; 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, &quot;SET %s %d&quot;, &quot;test1&quot;, count);\n printf(&quot;SET: %d\\n&quot;, count);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n }\n count++ ;\n freeReplyObject(reply);\n sleep(1);\n }\n\n redisClusterFree(cc);\n redisFreeSSLContext(ssl);\n return 0;\n}\n</code></pre>\n<p>2.以下是读取 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行读取,请注意配置的地址为 ElastiCache 集群的 configuration endpoint ,另外修改不同的 keyname。</p>\n<pre><code class=\"lang-\">#include &quot;hircluster.h&quot;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n struct timeval timeout = {1, 500000}; // 1.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, &quot;&lt;No TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;&quot;); \n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterConnect2(cc);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count &lt; 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, &quot;GET %s&quot;, &quot;test1&quot;);\n if(reply &amp;&amp; reply-&gt;str) {\n printf(&quot;GET: %s\\n&quot;, reply-&gt;str);\n \n }\n freeReplyObject(reply);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n }\n count++;\n sleep(1);\n }\n\n redisClusterFree(cc);\n return 0;\n}\n</code></pre>\n<p><strong>基本功能测试</strong></p>\n<p>1.使用如下命令编译代码:</p>\n<p><code>gcc hiredisc-naw1.c -o hiredisc-naw1 -L./build -lhiredis_cluster /usr/local/lib/libhiredis.a -L~/hiredis -lhiredis_ssl</code></p>\n<p>2.运行程序并监控 ElastiCache 未开启 TLS 及 Auth 集群的CurrConnections 指标,可以看到负载被均衡地发送到了6个节点上,即 hiredis-cluster 可以完成读写分离和负载均衡的工作:</p>\n<p>我的代码在~/hiredis-cluster 路径下面,所以</p>\n<pre><code class=\"lang-\">cd hiredis-cluster/ &amp;&amp;./hiredisc-naw1\ncd hiredis-cluster/ &amp;&amp;./hiredisc-naw2\ncd hiredis-cluster/ &amp;&amp;./hiredisc-naw3\ncd hiredis-cluster/ &amp;&amp;./hiredisc-nar1\ncd hiredis-cluster/ &amp;&amp;./hiredisc-nar2\ncd hiredis-cluster/ &amp;&amp;./hiredisc-nar3\n</code></pre>\n<p><img src=\"https://dev-media.amazoncloud.cn/f7983fcb5f8b4886bb8c4df046239a89_image.png\" alt=\"image.png\" /></p>\n<h4><a id=\"23_TLS_auth___ElastiCache__363\"></a><strong>2.3. 开启传输中加密(TLS)及 auth 的 ElastiCache 集群测试</strong></h4>\n<p>Amazon ElastiCache 传输中加密是一项可选功能,它允许您在数据最脆弱时候(从一个位置传输到另一个位置时)提高数据的安全性。由于在终端节点加密和解密数据时需要进行一些处理,因此启用传输中加密会对性能产生一些影响。应对使用和不使用传输中加密的数据进行基准测试,以确定对使用案例的性能影响。 关于 TLS 加密这一块,具体可以参考条条大路通罗马- 使用 redission 连接 Amazon ElastiCache for redis 集群。</p>\n<p><strong>加载测试数据</strong></p>\n<p>1.使用 redis-cli 登录开启了 TLS 和 auth 的 ElastiCache 集群并通过 cluster nodes 命令获取 slots 在 shards 中的分布,通过 redis-cli 工具连接集群并进行操作的具体方法请见使用 redis-cli 连接到 Redis 集群。</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/b1e1e21bd835462babe59759516d6fff_image.png\" alt=\"image.png\" /></p>\n<p>redis-cli -c -h &lt;No TLS and auth ElastiCache configuration endpoint&gt; -p &lt;port&gt;</p>\n<p>2.分别生成 testka,testb,testc 三个 key,可以通过其 slot 值结合上文各个 shard 上 slot 分布信息,判断出 3 个 key 恰好分布在shard1,shard2 和 shard3 上</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/973b537d1e7c47778976bd56277fc57a_image.png\" alt=\"image.png\" /></p>\n<p><strong>C代码</strong></p>\n<p>1.以下是写入 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行写入,请注意配置的地址为 ElastiCache 集群的 configuration endpoint,设置密码,另外修改不同的 keyname。这里需要注意的是 hiredis-cluster 对SSL的支持,可以分为两种方式,一种是指定 SSL 证书路径,另一种是指定证书,对于 EC2 访问 ElastiCache 的情况,指定证书路径即可, 具体可参考 hiredis 的文档。</p>\n<pre><code class=\"lang-\">#include &quot;hiredis_cluster/hircluster.h&quot;\n\n#include &quot;hiredis/hiredis_ssl.h&quot;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\n#define CLUSTER_NODE_TLS &quot;:&lt;Enable TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;&quot;\n#define CLUSTER_PASSWORD &quot;&lt;PASSWORD&gt;&quot;\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n\n redisSSLContext *ssl;\n redisSSLContextError ssl_error;\n\n redisInitOpenSSL();\n ssl = redisCreateSSLContext(NULL, &quot;/etc/ssl/certs&quot;, NULL, NULL,\n NULL, &amp;ssl_error);\n if (!ssl) {\n printf(&quot;SSL Context error: %s\\n&quot;, redisSSLContextGetError(ssl_error));\n exit(1);\n }\n\n struct timeval timeout = {5, 500000}; // 5.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS);\n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterSetOptionEnableSSL(cc, ssl);\n redisClusterSetOptionPassword(cc, CLUSTER_PASSWORD);\n redisClusterConnect2(cc);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count &lt; 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, &quot;SET %s %d&quot;, &quot;test1&quot;, count);\n printf(&quot;SET: %d\\n&quot;, count);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n }\n count++ ;\n freeReplyObject(reply);\n sleep(1);\n }\n\n redisClusterFree(cc);\n redisFreeSSLContext(ssl);\n return 0;\n}\n</code></pre>\n<p>2.以下是读取 ElastiCache 集群的代码, 针对于 Elasticache 三个shard 上的 test1,test2,test3 三个 key 进行读取,请注意配置的地址为 ElastiCache 集群的 configuration endpoint ,另外修改不同的 keyname 。</p>\n<pre><code class=\"lang-\">#include &quot;hiredis_cluster/hircluster.h&quot;\n\n#include &quot;hiredis/hiredis_ssl.h&quot;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\n#define CLUSTER_NODE_TLS &quot;&lt;Enable TLS and auth ElastiCache configuration endpoint&gt;:&lt;port&gt;&quot;\n#define CLUSTER_PASSWORD &quot;&lt;PASSWORD&gt;&quot;\n\nint main(int argc, char **argv) {\n UNUSED(argc);\n UNUSED(argv);\n\n redisSSLContext *ssl;\n redisSSLContextError ssl_error;\n\n redisInitOpenSSL();\n ssl = redisCreateSSLContext(NULL, &quot;/etc/ssl/certs&quot;, NULL, NULL,\n NULL, &amp;ssl_error);\n if (!ssl) {\n printf(&quot;SSL Context error: %s\\n&quot;, redisSSLContextGetError(ssl_error));\n exit(1);\n }\n\n struct timeval timeout = {5, 500000}; // 5.5s\n\n redisClusterContext *cc = redisClusterContextInit();\n redisClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS);\n redisClusterSetOptionConnectTimeout(cc, timeout);\n redisClusterSetOptionRouteUseSlots(cc);\n redisClusterSetOptionEnableSSL(cc, ssl);\n redisClusterSetOptionPassword(cc, CLUSTER_PASSWORD);\n redisClusterConnect2(cc);\n if (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n // handle error\n exit(-1);\n }\n\n int count = 0;\n while(count &lt; 10000){\n redisReply *reply = (redisReply *)redisClusterCommand(cc, &quot;GET %s&quot;, &quot;test1&quot;);\n if(reply &amp;&amp; reply-&gt;str) {\n printf(&quot;GET: %s\\n&quot;, reply-&gt;str);\n \n }\n freeReplyObject(reply);\nif (cc &amp;&amp; cc-&gt;err) {\n printf(&quot;Error: %s\\n&quot;, cc-&gt;errstr);\n }\n count++;\n sleep(1);\n }\n\n redisClusterFree(cc);\n redisFreeSSLContext(ssl);\n return 0;\n}\n</code></pre>\n<p><strong>基本功能测试</strong></p>\n<p>1.使用如下命令编译代码:</p>\n<p><code>gcc hiredisc-aw1.c -o hiredisc-aw1 -L./build -lhiredis_cluster /usr/local/lib/libhiredis.a -L~/hiredis -lhiredis_ssl</code></p>\n<p>2.运行程序并监控 ElastiCache 开启 TLS 及 Auth 集群的CurrConnections 指标,可以看到负载被均衡地发送到了 6 个节点上,即 hiredis-cluster 可以完成读写分离和负载均衡的工作:</p>\n<p>我的代码在~/hiredis-cluster 路径下面,所以</p>\n<pre><code class=\"lang-\">cd hiredis-cluster/ &amp;&amp;./hiredisc-aw1\ncd hiredis-cluster/ &amp;&amp;./hiredisc-aw2\ncd hiredis-cluster/ &amp;&amp;./hiredisc-aw3\ncd hiredis-cluster/ &amp;&amp;./hiredisc-ar1\ncd hiredis-cluster/ &amp;&amp;./hiredisc-ar2\ncd hiredis-cluster/ &amp;&amp;./hiredisc-ar3\n</code></pre>\n<p><img src=\"https://dev-media.amazoncloud.cn/ea346774103842b5b705aac8be576157_image.png\" alt=\"image.png\" /></p>\n<h3><a id=\"_525\"></a><strong>三级标题</strong></h3>\n<h4><a id=\"31__TLS__auth__ElastiCache_failover__527\"></a><strong>3.1. 未开启 TLS 及 auth 的 ElastiCache failover 测试</strong></h4>\n<p>登录 console,对 Shard1 进行 failover,然后观测我们的C程序的输出, 此过程大约持续12s ((观测所得)),</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/a5386fa2fe29449da320a5dc499dce16_image.png\" alt=\"image.png\" /></p>\n<p>从 ElastiCache 集群的每个节点的 CurrConnections 指标截图同样可以看出,hiredis-cluster 将读写请求都发送到了 shard1 新的主节点上:</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/06f58f2d51b94171bd98a251c3eeb975_image.png\" alt=\"image.png\" /></p>\n<h4><a id=\"32__TLS__auth__ElastiCache_failover__537\"></a><strong>3.2. 开启 TLS 及 auth 的 ElastiCache failover 测试</strong></h4>\n<p>登录 console,对 Shard1 进行 failover,然后观测我们的C程序的输出,这个时间大概 10s 左右(观测所得),</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/576c49d8eab943319ba388e67c85e9b6_image.png\" alt=\"image.png\" /></p>\n<p>从 ElastiCache 集群的每个节点的 CurrConnections 指标截图同样可以看出, hiredis-cluster 将读写请求都发送到了 shard1 新的主节点上:</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/7fd624e9c6834143a0041be1cc5c6c99_image.png\" alt=\"image.png\" /></p>\n<p><strong>failover 测试中发生问题以及解决方案</strong></p>\n<p>在做 failover 的时候发现一个问题,在 failover 过程中,有的时候client 端直接断开链接,有的时候成功,后来经过分析 hiredis 的代码得知需要把 timeout 时间调整下,所以从 1.5s 调整为 5.5s, 注意这个值并不是最佳实践,是根据测试得来的。</p>\n<h3><a id=\"4__551\"></a><strong>4. 结束语</strong></h3>\n<p>在本博客中,主要使用C语言通过 hiredis-cluster 连接和操作ElastiCache 集群,从简单的 Demo 中我们可以看到 hiredis-cluster 可以很好地支持 ElastiCache 集群开启 TLS 及 auth 等功能,并自动完成读写分离,负载均衡,failover 等工作,有助于我们便捷,高效地使用 ElastiCache。</p>\n<h3><a id=\"5_555\"></a><strong>5.参考</strong></h3>\n<p>Hiredis-cluster 文档:<a href=\"https://github.com/Nordix/hiredis-cluster\" target=\"_blank\">https://github.com/Nordix/hiredis-cluster</a></p>\n<p>Hiredis 文档: <a href=\"https://github.com/redis/hiredis\" target=\"_blank\">https://github.com/redis/hiredis</a></p>\n<p>Amazon Elasticache : <a href=\"https://aws.amazon.com/elasticache/redis/\" target=\"_blank\">https://aws.amazon.com/elasticache/redis/</a></p>\n<p>条条大路通罗马 —— 使用 redisson 连接 Amazon ElastiCache for redis 集群:<a href=\"https://aws.amazon.com/cn/blogs/china/connecting-amazon-elasticache-for-redis-cluster-using-redisson/\" target=\"_blank\">https://aws.amazon.com/cn/blogs/china/connecting-amazon-elasticache-for-redis-cluster-using-redisson/</a></p>\n<h3><a id=\"_565\"></a><strong>相关博客</strong></h3>\n<p><a href=\"https://aws.amazon.com/cn/blogs/china/connecting-amazon-elasticache-for-redis-cluster-using-redisson/\" target=\"_blank\">条条大路通罗马 —— 使用 redisson 连接 Amazon ElastiCache for redis 集群</a></p>\n<p><a href=\"https://aws.amazon.com/cn/blogs/china/use-redis-py-to-access-amazon-elasticache-for-redis-cluster/\" target=\"_blank\">条条大路通罗马 —— 使用 redis-py 访问 Amazon ElastiCache for redis集群</a></p>\n<p><a href=\"https://aws.amazon.com/cn/blogs/china/all-roads-lead-to-rome-use-go-redis-to-connect-amazon-elasticache-for-redis-cluster/\" target=\"_blank\">条条大路通罗马 —— 使用 go-redis 连接 Amazon ElastiCache for Redis 集群</a></p>\n<h3><a id=\"_572\"></a><strong>本篇作者</strong></h3>\n<p><img src=\"https://dev-media.amazoncloud.cn/ab27d6fa467b473aa8e689ab6eaad5ea_image.png\" alt=\"image.png\" /></p>\n<h4><a id=\"_576\"></a><strong>冯秋爽</strong></h4>\n<p>Amazon 解决方案架构师,负责跨国企业级客户基于 Amazon 的技术架构设计、咨询和设计优化工作。在加入 Amazon 之前曾就职于 IBM 、甲骨文等 IT 企业,积累了丰富的程序开发和数据库的实践经验。</p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭
contact-us