作为推出国内首创可编程 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 操作系统焕然一新

Golang 常见设计模式之单例模式

又拍云 Redis 的改进之路的更多相关文章

  1. Flink 在又拍云日志批处理中的实践

    日前,由又拍云举办的大数据与 AI 技术实践|Open Talk 杭州站沙龙在杭州西溪科创园顺利举办.本次活动邀请了有赞.个推.方得智能.又拍云等公司核心技术开发者,现场分享各自领域的大数据技术经验和 ...

  2. 又拍云张聪:OpenResty 动态流控的几种姿势

    2019 年 1 月 12 日,由又拍云.OpenResty 中国社区主办的 OpenResty × Open Talk 全国巡回沙龙·深圳站圆满结束,又拍云首席架构师张聪在活动上做了< Ope ...

  3. 【实战分享】又拍云 OpenResty / Nginx 服务优化实践

    2018 年 11 月 17 日,由 OpenResty 主办的 OpenResty Con 2018 在杭州举行.本次 OpenResty Con 的主题涉及 OpenResty 的新开源特性.业界 ...

  4. 又拍云叶靖:OpenResty 在又拍云存储中的应用

    2019 年 7 月 6 日,OpenResty 社区联合又拍云,举办 OpenResty × Open Talk 全国巡回沙龙·上海站,又拍云平台开发部负责人叶靖在活动上做了<OpenRest ...

  5. 跬步千里 —— 阿里云Redis bitfield命令加速记

    link:https://developer.aliyun.com/article/757841 在一次阿里云客户问题解决中,通过给Redis添加bitfield_ro命令,解决了Redis官方bit ...

  6. 用StackExchange.Redis客户端连接阿里云Redis服务遇到的问题

    阿里云推荐的Redis服务.NET客户端是ServiceStack.Redis,但ServiceStack.Redis不支持异步,不支持.NET Core,于是尝试使用StackExchange.Re ...

  7. ajax异步上传到又拍云的实例教程

    作者:白狼 出处:www.manks.top/article/async_upload_to_upyun 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否 ...

  8. 阿里云 Redis 服务遇到的问题

    ERR unknown command eval 说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 异常详细信息: St ...

  9. iOS UPYUN(又拍云)使用总结

    UPYUN,原来没用过,上个周用了一次,觉得蛮方便的,对于个人开发者,且没有服务器的,上传图片和文件,是个不二选择. 首先,先明白原理: 1.又拍云有一个上传空间,在这个空间里,有空间名称.密钥,其他 ...

随机推荐

  1. Java8 新特性,打破你对接口的认知

    Java 8 之前,接口里面只能写抽象方法,不能写实现方法 Java 8 开始是可以有方法实现的,可以在接口中添加默认方法和静态方法 默认方法用 default 修饰,只能用在接口中,静态方法用 st ...

  2. Revit二次开发之添加选项卡和按钮

      我们日常在revit开发中经常会用到按钮,可以通过revitAPI提供的接口创建按钮,今天我简单介绍一下两种按钮,一种是单命令按钮,另一种是含下拉菜单的按钮,包括创建他们的方法. 实现方法 1.实 ...

  3. C++中 指针的指针是什么?指针的引用又是什么?你可能需要看看这篇文章

    关于变量的定义 我们都知道变量的定义包括一个基本数据类型(base type)和一组声明符,在同一条定义语句中,输入基本数据类型不同,但是声明符的形式却可以不同. //如:i是一个int的整数,a是一 ...

  4. 新华三Gen10服务器ILO 5 安装中文语言包

    ILO 5 安装中文语言包 在官网下载语言包文件,并解压 选择firmware&OS software,点击右侧的update firmware 选择本地文件,浏览到语言包里面的lpk文件,点 ...

  5. VMware安装Ubuntu20(图文教程,超详细)

    VMware安装Ubuntu20(图文教程,超详细) 此文讲述使用 VMware 工具安装 Ubuntu 系列虚拟机,不同位数和不同版本的 Ubuntu 安装过程相差无几,这里以 Ubuntu20 6 ...

  6. 团队Arpha4

    队名:观光队 组长博客 作业博客 组员实践情况 王耀鑫 **过去两天完成了哪些任务 ** 文字/口头描述 完成服务器连接数据库部分代码 展示GitHub当日代码/文档签入记录 接下来的计划 服务器网络 ...

  7. 10个最危险的Linux命令,希望你牢记在心

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 来源:Linux迷链接:https://www.linu ...

  8. 红旗 Linux 桌面操作系统11来了:支持国产自主CPU,全新UI风格设计,兼容面广...

    链接:https://reurl.cc/g8ke9X 红旗Linux桌面操作系统11将于1月10日开放预览版的下载,新版本具有良好的硬件兼容,支持多款国产自主CPU品牌,同时还具有丰富的外设支持及海量 ...

  9. 手把手教你在Linux中快速检测端口的 3 个小技巧

    一个执着于技术的公众号 前言 无论是要解决网络连接问题还是配置防火墙,第一件事是要检查系统实际打开了哪些端口. 本文介绍了几种快速查找 Linux 系统上哪些端口向外部开放的方法. 什么是开放端口 监 ...

  10. 【软件构造】Mutable类型与Immutable类型

    [软件构造]Mutable类型与Immutable类型 1.前言 在软件构造这门课中,对mutable类型和immutable类型的深入理解,有助于后续ADT.可维护性.可复用性的学习,因此我们有必要 ...