又拍云 Redis 的改进之路
作为推出国内首创可编程 CDN 服务的专业云服务提供商,又拍云利用 CDN 边缘网络规模和性能,允许客户自定义编写规则来满足常用业务场景。而为了保证这些源数据,如边缘重定向、请求限速、自定义错误页面、访问防盗链控制、 HTTP 头部管理等,能快速同步到边缘的节点服务器,在对比了多个方案以后,又拍云于 2014 年初开始使用 Redis2.8 版本作为数据同步的解决方案。
最初的架构如下:

在继续谈 Redis 改进前,我们要先了解一下技术债。这里说的技术债指的是技术负债,通常开发人员为了加速软件开发,在应该采用最佳方案时可能进行妥协,改用短期内能加速软件开发的方案。而这种方案在未来给自己带去了额外开发负担。这种虽然眼前看起来可以得到好处,但必须在未来偿还的选择,就像债务一样,所以被叫做技术债。
而我们上面说到的这个方案就埋下了技术债的引子。在过去的几年里它虽然起到重要的作用,但架构的缺点明显,而且随着边缘服务器数量和同步数据量的增加,再加上服务器硬件的老化故障等等原因,造成了很多问题,比如如下问题:
出于安全考虑,相互 Redis 之间的通信数据都需要加密,但 Redis 本身不支持 SSL 加密。因此所有的边缘服务器都必须通过 stunnel 套接做中转服务器。然而实际工作状态下,stunnel 的性能不足,导致服务器 CPU 负载过高。
Redis 的数据主从都是长连接且尽量保持从同一源做同步,因此早期边缘服务器都是通过域名解析的方式来获取源服务器的 IP 地址。这样的好处是实施部署简单,缺点是 DNS 无法获知后端服务器的处理能力,造成每台机器上的长连接负载不均衡。而且后端服务出故障后 DNS 也无法自动处理, 即便及时对 DNS 进行了切换解析,也会因为 TTL 生效前的真空期引起数据不一样, 导致只能使用旧数据应急。
因为历史遗留原因, 边缘 Redis 版本大都是 2.x 低版本,而低版本只能通过 sync 做全量同步。因此中转服务器和主服务器的异常都会造成全网的雪崩效应,从而同步阻塞,无法快速同步元数据到边缘。
因为早期 Redis 只有主从模式可以采用,也没有实现哨兵和集群改造。所以让如今主服务器成为了单点风险,很容易造成源头上的重大故障。
因为之前妥协导致的问题和副作用,以至于我们现在必须要付出额外的时间和精力进行重构,把架构改善为最佳实现方式。
我们把改造过程分成几个步骤:
加强 SSL 的安全防护,尽可能升级到 OpenSSL 最新的稳定版本
SSL 可能是大家接触比较多的互联网安全协议之一,一般网站地址用了“https://”开头,就是采用了 SSL 安全协议。OpenSSL 是一种开放源码的 SSL 实现,用来实现网络通信的高强度加密,现在被广泛地用于各种网络应用程序中。如此重要的项目多年来始终面临着资金和人手不足的窘境,多数工作都要由为数不多的黑客和爱好者及志愿者来完成。幸好现在纳入 Linux 基金会资金资助对象,不过依然有新漏洞不断暴露,需要及时关注和跟进。
参考最新的 OpenSSL 漏洞危险等级报告:

鉴于 RC4 算法安全漏洞太多,建议编译时选择禁用。
使用最新的 stunnel 版本,优化性能,基于安全 OpenSSL 依赖库,支持 TLSv1.2+ 以上
从下图的红色框中可以看出,stunnel 在某些算法下的性能是最强的,所以在配置文件中推荐优先使用:

./configure --prefix=/opt/stunnel --with-ssl=/opt/openssl
来看一下推荐配置中的优化选项:
verify = 3
sslVersionMax = TLSv1.3
sslVersionMin = TLSv1.2
options = NO_SSLv2
options = NO_SSLv3
.......
ciphers = ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE
可以通过亚洲诚信的网站来做 HTTPS 的可信等级检测和验证。

编译最新的 Redis-6.2.x 稳定版,功能强大丰富且无需依赖高版本的 GCC
Redis6.2 与 7.0 对比来看的话,肯定是 7.0 版本更为强大一点。Redis7.0 几乎包括了对各个方面的增量改进,其中最值得注意的是 Redis Functions、ACLv2、command introspection 和 Sharded Pub/Sub。7.0 版添加了近 50 个新命令和选项来支持这种演变并扩展 Redis 的现有功能。
但是尽管 Redis7.0 更加强大,可是综合考虑到与原来的 Redis 代码的完全兼容性,以及生产环境的稳定,我们最终选择了 Redis6.2。因为 Redis6.2 的优点也足够多,功能也很强大,而且更能满足我们生产环境的要求,比如:
多线程 IO(Threaded I/O)
众多新模块(modules)API
更好的过期循环(expire cycle)
支持 SSL
ACLs 权限控制
RESP3 协议
客户端缓存(Client side caching)
无盘复制&PSYNC2
Redis-benchmark 支持集群
Redis-cli 优化、重写 Systemd 支持
Redis 集群代理与 Redis6 一同发布(但在不同的 repo)
RDB 更快加载
SRANDMEMBER 和类似的命令具有更好的分布
STRALGO 命令
带有超时的 Redis 命令更易用
重点介绍一下 PSYNC2 的特性,这也是我们架构改进升级的重点特性之一。
在 Redis cluster 的实际生产运营中,实例的维护性重启、主实例的故障切换(如cluster failover)操作都是比较常见的(如实例升级、rename command 和释放实例内存碎片等)。而在 Redis4.0 版本前,这类维护性的处理 Redis 都会发生全量重新同步,导到性能敏感的服务有少量受损。而 PSYNC2 主要让 Redis 在从实例重启和主实例故障切换场景下,也能使用部分重新同步。
直接下载源代码编译:
# make BUILD_TLS=no
推荐配置,添加以下选项增强性能:
io-threads-do-reads yes
io-threads 8
aof-use-rdb-preamble yes
在我们的测试过程中,发现 Redis+TLS 有几个问题:
Redis 开启 TLS后,性能下降 30%。
Redis 对 OpenSSL 的强依赖性. 考虑到 OpenSSL 的过往高危漏洞不断, 如果要不断修复漏洞要重新编译 Redis,导致运维更新成本过高。
Redis 升级后, 要重新同步数据, 增加了出故障的机率或让生产停摆。
所以, 我们还是决定使用第三方程序 stunnel 来加固安全,方便升级和修复漏洞。又不影响后端连接,从而保障了 Redis 的工作连续性和稳定可靠性。
基于 APISIX+TLS 托管,使用 TCP 的哈希一致性做负载均衡来替换 DNS 的轮询,效能显著
APISIX 使用 TCP 代理, 这部分直接配置后就可以使用,和 Redis 改造关系不大,我们就直接略过,大家可以直接看一下改造后的连接数统计截图。从实际的 APISIX 的连接数可以看出负载被数量均衡地分摊到了不同的后端,而且边缘服务器重启也利用 PSYNC2 做了快速的增量同步。

使用 Redis-shake 做定制化的数据同步
在架构改进的过程中,我们也看了 redis-shake 这个工具,它是阿里云 Redis&MongoDB 团队开源的用于 Redis数据同步的工具。它支持 解析、恢复、备份、同步 四个功能。给大家主要介绍同步 sync:
恢复 restore:将 RDB 文件恢复到目的 Redis 数据库。
备份 dump:将源 Redis 的全量数据通过 RDB 文件备份起来。
解析 decode:对 RDB 文件进行读取,并以 json 格式解析存储。
同步 sync:支持源 Redis 和目的 Redis 的数据同步,支持全量和增量数据的迁移。
同步 rump:支持源 Redis 和目的 Redis 的数据同步,仅支持全量的迁移。采用 scan 和 restore 命令进行迁移,支持不同云厂商不同 Redis 版本的迁移。
我们原来有一个做过源代码修改过的 Redis,只会同步想要的空间。虽然好用,但还是需要在新代码上重新编译一个,可是原来的负责人已经找不到了。这也是很多年久失修项目的通病, 但通过 redis-shake 这样的开源工具,只要通过它简单配置一下就可以实现我们想要的功能:
- filter.db.whitelist / blacklist
- filter.key.whitelist / blacklist
- filter.command.whitelist / blacklist
现在的架构及未来的展望

在现在的架构中,我们在原来的三层架构基础上,又拆分和强化了三层架构:
DNS 层解析到 VIP,VIP 利用了 BGP/OSPF 的动态网关路由协议,对应后面一组服务器集群服务。
负载均衡层:利用 “apisix ”+ “tls1.2+ ”+ “tcp的哈希一致性连接”,把 Redis 的主从连接均衡,故障转移。
边缘 CDN 节点,利用 Redis 高版本所带来的技术红利,psync 的增量同步,加上 stunnel+tls1.2 实现了加密传输。
下一个阶段, 还要继续把数据中心的 Redis 主改造成 Redis 哨兵模式(考虑到程序代码要对哨兵模式做兼容性改造, 第一阶段先不上, 一切都为了生产环境中的稳定性)。
参考文档:
如何检查网站的 TLS 版本:https://wentao.org/post/2020-11-29-ssl-version-check/
Redis 特性之复制增强版 PSYNC2:https://www.modb.pro/db/79478
通俗易懂的 Redis 架构模式详解:https://www.cnblogs.com/mrhelloworld/p/redis-architecture.html
推荐阅读
【实操干货】做好这 16 项优化,你的 Linux 操作系统焕然一新
又拍云 Redis 的改进之路的更多相关文章
- Flink 在又拍云日志批处理中的实践
日前,由又拍云举办的大数据与 AI 技术实践|Open Talk 杭州站沙龙在杭州西溪科创园顺利举办.本次活动邀请了有赞.个推.方得智能.又拍云等公司核心技术开发者,现场分享各自领域的大数据技术经验和 ...
- 又拍云张聪:OpenResty 动态流控的几种姿势
2019 年 1 月 12 日,由又拍云.OpenResty 中国社区主办的 OpenResty × Open Talk 全国巡回沙龙·深圳站圆满结束,又拍云首席架构师张聪在活动上做了< Ope ...
- 【实战分享】又拍云 OpenResty / Nginx 服务优化实践
2018 年 11 月 17 日,由 OpenResty 主办的 OpenResty Con 2018 在杭州举行.本次 OpenResty Con 的主题涉及 OpenResty 的新开源特性.业界 ...
- 又拍云叶靖:OpenResty 在又拍云存储中的应用
2019 年 7 月 6 日,OpenResty 社区联合又拍云,举办 OpenResty × Open Talk 全国巡回沙龙·上海站,又拍云平台开发部负责人叶靖在活动上做了<OpenRest ...
- 跬步千里 —— 阿里云Redis bitfield命令加速记
link:https://developer.aliyun.com/article/757841 在一次阿里云客户问题解决中,通过给Redis添加bitfield_ro命令,解决了Redis官方bit ...
- 用StackExchange.Redis客户端连接阿里云Redis服务遇到的问题
阿里云推荐的Redis服务.NET客户端是ServiceStack.Redis,但ServiceStack.Redis不支持异步,不支持.NET Core,于是尝试使用StackExchange.Re ...
- ajax异步上传到又拍云的实例教程
作者:白狼 出处:www.manks.top/article/async_upload_to_upyun 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否 ...
- 阿里云 Redis 服务遇到的问题
ERR unknown command eval 说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 异常详细信息: St ...
- iOS UPYUN(又拍云)使用总结
UPYUN,原来没用过,上个周用了一次,觉得蛮方便的,对于个人开发者,且没有服务器的,上传图片和文件,是个不二选择. 首先,先明白原理: 1.又拍云有一个上传空间,在这个空间里,有空间名称.密钥,其他 ...
随机推荐
- Java学习day24
今天学习了IP地址.端口以及TCP/UDP通信协议 网络连接与过去的信件类似,需要知道对方的地址才能寄出去,在计算机网络中,我们的地址就是IP以及端口号 IP能用来唯一定位一台联网的计算机 本机的IP ...
- redis在物理机部署模式下如何进行资源[cpu、网卡]隔离
上周末晚上运营做直播,业务代码不规范,访问1个redis竟然把1台服务器的网卡打满了,这台服务器上的其他redis服务都受到了影响.之前没有做这方面的预案,当时又没有空闲的机器可以迁移,在当时一点办法 ...
- python中的嵌套
嵌套:将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套.既可以在列表中嵌套字典,也可以在字典中嵌套列表,甚至在字典中嵌套字典. 一.列表中嵌套字典 1)一般创建方式: student_ ...
- c#中判断类是否继承于泛型基类
在c#中,有时候我们会编写类似这样的代码: public class a<T> { //具体类的实现 } public class b : a<string>{} 如果b继承a ...
- js常用框架原理
(function(){ //存储已经创建的模块 var moduleMap = {}; //判断是否已经加载过 var fileMap = {}; ...
- partOne讲解思路
讲解思路 分解:把一个复杂的大问题,拆解成更可执行.更好理解的小步骤. 模式识别:找出相似模式,高效解决细分问题. 抽象:聚焦最重要的信息,忽视无用细节. 算法:设计一步一步的解决路径,解决整个问 ...
- 软件工程-UML各种图总结-精华
UML(Unified Modeling Language)是一种统一建模语言,为面向对象开发系统的产品进行说明.可视化.和编制文档的一种标准语言.下面将对UML的九种图+包图的基本概念进行介绍以 ...
- XCTF练习题---MISC---wireshark1
XCTF练习题---MISC---wireshark1 flag:flag{ffb7567a1d4f4abdffdb54e022f8facd} 解题步骤: 1.观察题目,下载附件 2.得到一个wire ...
- 树莓派开发笔记(十三):入手研华ADVANTECH工控树莓派UNO-220套件(二):安装rtc等驱动
前言 前面运行了系统,本篇是安装对应套装的驱动,使rtc等外设生效,树莓派本身是不带rtc外设的. UNO-220-P4N1AE 驱动下载 官方下载:https://www.advan ...
- PHP 运行 mkdir() Permission Denied 的原因
使用lamp,在上传文件时,PHP执行 mkdir($path) , 出现没有权限的错误. 解决: 本次使用的时yii框架,所以首先确保 是apache的用户对web目录有权限,然后再给此用户加 r ...