https://www.infoq.cn/news/s2fa603MsEENsCmibTYI

长期以来,NGINX 可以说是网站安全和托管服务提供商 Cloudflare 的核心,是其所使用的基础软件的一部分。

“Cloudflare 将 NGINX 用于其提供的所有 Web 服务,并在世界各地的数千台机器上使用它作为反向代理服务器。”“我们选择 NGINX 主要是因为它的性能。”Cloudflare CTO John Graham-Cumming 曾如此阐述 NGINX 对 Cloudflare 的重要性。

不过,如今 Cloudflare决定放弃 NGINX ,转而使用内部开发的 Pingora。理由也和性能有关。

按照官方的说法,随着 Cloudflare 的发展壮大,NGINX 已经无法满足他们的现实业务需求。“虽然 NGINX 多年来一直表现良好,但时间推移之下。其局限性也在我们的持续迭代、规模扩张之下暴露无遗。我们既无法获得理想的性能,NGINX 也没法为高度复杂的环境提供必要的功能支持。”Cloudflare 于 9 月 14 日发布的博文中写道。

Pingora 是 Cloudflare 工程师用 Rust 编写的全新 HTTP 代理系统,专为 Cloudflare 用例及业务规模设计。据介绍,它每天处理超过 1 万亿条请求,提高系统性能之余,也为 Cloudflrae 客户带来不少新功能。更重要的是,它运行所占用的 CPU 和内存资源只相当于原有代理基础设施的三分之一。

目前 Pingora 尚未开源,官方称将找个合适的时机再对外分享。

以下内容源自 Cloudflare,其详细讲述了换掉旧代理的原因,以及他们是如何开发出 Pingora 的。

为什么要构建新代理

多年以来,NGINX 的种种局限性已经严重影响到我们的业务运营。虽然先后优化或缓解了部分限制,但仍有一部分问题始终得不到完美解决。

架构限制开始拖累性能

NGINX worker(进程)架构在我们的用例中存在缺陷,而且已经损害了 Cloudflare 的性能和效率。

首先,在 NGINX 当中,每条请求只能由单个 worker 处理。这会导致各 CPU 核心的负载不均衡,进而拖慢处理速度。

由于这种请求进程锁定效应,一旦出现高强度 CPU 操作或阻塞 IO 任务的请求,那么其他请求的处理速度也会受到影响。我们已经投入了不少精力尝试解决,但收效不佳。

对我们的用例来说,NGINX 中最大的麻烦还在于糟糕的连接重用机制。我们的设备与原始服务器间通过 TCP 连接来代理 HTTP 请求。连接重用会重复使用连接池中包含的原有连接,由此跳过建立新连接所需要的 TCP 和 TLS 握手,从而缩短 TTFB(第一字节时间)。

然而,NGINX 连接池是按 worker 划分的。当请求到达特定 worker 时,其只能重用该 worker 之内的连接。因此当我们添加更多 NGINX worker 进行扩容时,连接重用率就会变得越来越差,导致大量连接分散在所有进程的多个隔离池内。于是 TTFB 延长了、需要维护的连接数量增加了,我们自己乃至客户的资源(成本)也被白白浪费掉了。

可以看到,NGINX 中的 worker/进程模型才是罪魁祸首,所以开发新代理就成了从根源上解决问题的最佳途径。

难以添加某些功能类型

NGINX 其实是款非常出色的 Web 服务器、负载均衡器和简单网关。问题是 Cloudflare 的需求远不止于此。以往,我们常常围绕 NGINX 构建自己需要的各项功能,但同时还要避免同 NGINX 的上游代码库发生严重分歧,这相当于是戴着脚镣跳舞、非常痛苦。

例如,当请求重试/失败时,我们往往希望能将请求发送到具有不同请求标头集的其他服务器处。但 NGINX 并不允许这样的操作,所以我们就得投入时间和精力想办法突破 NGINX 的限制。

除了设计上的限制之外,NGINX 的编程语言要求也让我们颇为头痛。NGINX 是纯用 C 语言编写的,因此在设计上不具备内存安全性。在使用第三方代码库时经常会出错,即使对经验丰富的工程师来说,也很容易闹出内存安全问题。我们当然希望能尽量回避掉这些问题。

我们用过的另一种补充性语言是 Lua,它的风险更低,但性能也比较差。另外,在处理复杂的 Lua 代码和业务逻辑时,经常会出现静态类型缺失的问题。

最后,NGINX 社区不太活跃,开发团队往往像在“闭门造车”。

决定自己开辟一条道路

过去几年以来,随着我们不断扩大客户群体和功能集,Cloudflare 也一直在认真评估以下三种选项:

  1. 继续投资 NGINX,并尝试通过 fork 让它能 100%满足我们的需求。我们已经拥有必要的专业知识,但考虑到设计之初的架构限制,恐怕要投入大量资源才能根据自身需求完成全面重建。

  2. 迁移至其他第三方代理选项。市面上并不缺乏好的项目,比如 envoy 等。但我们担心再过几年,同样的问题也许会在那些项目身上重演。

  3. 从零开始构建内部平台与框架。这个选项的效果肯定是最好的,问题就是占用的工程资源和产生的前期投入也最多。过去几年来,我们每个季度都会对这些选项开展评估,但始终没找到最有说服力的答案。于是我们继续走阻力最小的道路,不断增强 NGINX。但团队中关于自建代理的呼声开始涌现,大家越来越觉得这种方式虽然投资较大,但最终回报是值得的。于是我们开始从零入手构建代理,希望能设计出完美匹配自身需求的代理方案。

Pingora 项目

设计决策

为了让代理快速、高效且安全地处理每秒数百万条请求,我们先得做出一系列重要的设计决策。

首先,我们选择用 Rust 编写这个项目。因为它能够在不影响性能的前提下,以内存安全的方式带来可与 C 语言比肩的极佳性能。

虽然市面上已经有不少很棒的第三方 HTTP 库,例如 hyper,但我们还是决定自行构建库。理由很简单,我们想要最大限度提升 HTTP 流量处理的灵活性,并确保按照自己的节奏推动创新。

在 Cloudflare,我们需要面对几乎是整个互联网的流量,必须支持种种稀奇古怪、不符合 RFC 的 HTTP 流量。这也是 HTTP 社区和 Web 领域的常见难题,即如何在严格遵循 HTTP 规范的同时,适应潜在遗留袖或服务器同广泛生态系统间的细微差别与紧张关系。

在 RFC 9110 中,HTTP 状态码被定义成一个三位整数,通常区间在 100 到 599 之间。Hyper 就是这样一种实现。但也有不少服务器支持使用 599 到 999 之间的状态码。这种冲突曾引发过激烈的争论,虽然 hyper 团队最终接受了这一变更,但如果不接受其实也没有办法——毕竟双方都有自己的道理可讲。而这,还只是我们需要支持的种种不合规行为中的冰山一角。

为了满足 Cloudflare 在 HTTP 生态系统中主导性地位的要求,我们必须建立起一个健壮、宽松、可定制的 HTTP 库,适应互联网上的狂野法则、支持种种不合规用例。好在由于是内部原创,所以至少决定权把握在我们自己手中。

下一项设计决策则跟工作负载调度系统有关。我们选择了多线程、而非多进程,目的是为了轻松实现资源共享,特别是连接池共享。我们还决定用工作窃取机制来避免前文提到的某些性能问题。事实证明,Tokio 异步运行时特别符合我们的需求。

最后,我们希望这个项目能直观些、对开发者们友好些。我们要开发的并不是最终产品,而是可以进一步扩展的平台,允许在其上构建更多功能。于是,我们决定提供一个类似于 NGINX/OpenResty 的基于“请求生命周期”事件的可编程接口。举例来说,可以后续编写“请求过滤器”帮助开发人员在收到请求标头时,运行相应代码来修改或拒绝请求。通过这样的设计,我们就能清晰地把业务逻辑和通用代理逻辑分离开来,同时保证之前接触 NGINX 的开发人员也能轻松转向 Pingora、迅速提高工作效率。

Pingora 在生产中速度更快

让我们快进到现在,Pingora 已经在处理几乎一切需要与源服务器交互的 HTTP 请求(例如缓存未命中的情况),我们在此期间也收集到了大量性能数据。

首先,我们来看看 Pingora 如何推动客户流量提速。Pingora 上的总体流量显示,TTFB 中位数降低了 5 毫秒,第 95 百分位 TTFB 降低了 80 毫秒。这当然不是因为我们的代码运行更快了,毕竟原封不动的旧服务现在也可以将请求响应控制在亚毫秒范围内。

这样的节约源自新架构,特别是它跨所有线程实现连接共享的能力。凭借着更好的连接重用率,我们在 TCP 和 TLS 握手上耗费的时间大为缩短。

与旧服务相比,Pingora 将全体客户的每秒新连接使用量降低至三分之一。对其中一家主要客户,其连接征用率从之前的 87.1%提升到了 99.92%,意味着新连接数量降低至原本的一百六十分之一。为了更直观地感受这种变化,大家不妨看看这个数字:自从使用 Pingora 以来,我们每天能够为客户和用户节约下长达 434 年的握手时间。

更多功能

有了工程师们熟悉的开发者友好接口,又消除了以往令人头痛的限制,我们自然可以快速开发出更多新功能。凭借新的协议等核心功能,我们现在能够为客户提供更多产品构建块。

例如,我们能够以相对简单的方式为 Pingora 添加 HTTP/2 上游支持,由此加快了向客户提供 gRPC 的速度。很明显,要想将这项功能添加进 NGINX,不只是涉及的工程量更大、甚至有可能压根无法实现。

最近,我们又公布了 Cache Reserve,Pingora 在其中使用 R2 存储作为缓存层。随着我们向 Pingora 添加更多功能,相信未来将提供更多开创性的新产品。

更高效率

在生产环境中, 面对同等流量负载的情况下,Pingora 所消耗的 CPU 和内存资源量与旧有服务相比,分别降低了约 70%和 67%。这样可观的资源节约源自以下几大要素。

与旧的 Lua 代码相比,我们的 Rust 新代码运行效率更高。更重要的是,二者在架构上也存在显著的效率差异。以 NGINX/OpenResty 为例,当 Lua 代码想要访问 HTTP 标头时,必从 NGINX C 结构中进行读取、分配一个 Lua 字符串,然后将该标头复制到 Lua 字符串内。最后,Lua 还得对这个新字符串进行垃圾回收。而 Pingora 不同,它能直接执行字符串访问,就这么简单。

多线程模型也让跨请求数据共享变得更加高效。NGINX 虽然也提供共享内存,但由于实现限制,每次共享内存访问都需要使用互斥锁,而且只能将字符串和数字放入共享内存。在 Pingora 中,大多数共享条目都能通过原子引用计数器后的共享引用进行直接访问。

至于 CPU 的节约,主要体现在前文提到的新连接创建量显著降低之上。不同于会造成高昂 TLS 握手成本的旧方案,Pingora 可以更多通过已建立的连接实现数据发送和接收。

更安全

快速安全地发布功能绝非易事,在 Cloudflare 这样庞大的运营规模下更是困难重重。我们几乎无法预测每秒要处理几百万条请求的分布式环境中可能发生哪些极端状况,毕竟模糊测试和静态分析根本就覆盖不到这样的场景。这时候,Rust 的内存安全语义保护挺身而出,在保护我们免受未定义行为困扰的同时,也让我们坚定相信自己的服务能够正确运行。

有了这些保障,我们就能更多关注自己的服务变更如何与其他服务或客户源进行交互。我们能以更快的节奏开发出新功能,不再受到内存安全和种种未知崩溃的拖累。

而一旦真的发生了崩溃,工程师们当然要花时间来诊断崩溃如何发生、背后又有怎样的原因。自 Pingora 运行以来,我们已经先后处理过数百万亿条请求,还没遇到过任何一次源自服务代码的崩溃问题。

事实上,Pingora 的崩溃可以说非常罕见,每次出现的问题都跟 Pingora 自身没什么关系。最近,我们在一次服务崩溃中发现了一个内核 bug,还在某些设备上发现了硬件问题。这种感觉简直神奇,长久以来最不稳定的软件元素现在却成了最可靠的部分,甚至足以支持我们发现硬件层面的内存 bug……之前无数次重大调试都找不到这些问题,原因自然是当时不等硬件崩溃,软件早已经坚持不住了。

总结

总而言之,我们建立起一套更快、更高效、更通用的内部代理,并把它当成现有及未来产品的运行平台。

借此机会,我们重新将视线集中到 Cloudflare 面临的问题、值得探索的优化空间,以及 Pingora 开发过程中积累下的重要经验教训与技术细节身上。我们也将回归开源精神,找个适合的机会与大家分享更多后续成果。

Pingora 是我们重构系统的一次最新尝试,但绝不会是最后一次。期待 Pingora 能成为我们全面系统重构的重要基石。

原文链接:

https://blog.cloudflare.com/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet/

[转帖]NGINX 局限太多,Cloudflare 最终放弃它并用 Rust 自研了全新替代品的更多相关文章

  1. 解决VMware虚拟机报错“无法连接MKS:套接字连接尝试次数太多,正在放弃”

    1.错误描述 在VMware中打开虚拟机时报错: "无法连接MKS:套接字连接尝试次数太多,正在放弃" 物理机操作系统: Windows 7 虚拟机操作系统: Kali Linux ...

  2. VMware Workstation “无法连接 MKS: 套接字连接尝试次数太多;正在放弃。” 解决方法【转】

    今天和往常一样打开电脑,打开VMware Workstation,打开其中的一台虚拟机,以前都是这么打开没有问题,今天打开虚拟机突然提示“无法连接 MKS: 套接字连接尝试次数太多:正在放弃.”. 经 ...

  3. Vmware Pro 14报错:无法连接 MKS: 套接字连接尝试次数太多;正在放弃。

    软件环境: 虚拟机软件:VMware Pro 14 母机操作系统:win7 客户机操作系统:CentOS 7     问题详情: 报错:无法连接 MKS: 套接字连接尝试次数太多:正在放弃.     ...

  4. [转帖]nginx配置ssl加密(单/双向认证、部分https)

    nginx配置ssl加密(单/双向认证.部分https) https://segmentfault.com/a/1190000002866627   nginx下配置ssl本来是很简单的,无论是去认证 ...

  5. [转帖]Nginx 的 TCP 负载均衡介绍

    Nginx 的 TCP 负载均衡介绍 https://www.cnblogs.com/felixzh/ 前几天同事问 nginx的代理 当时以为只有http的 现在看起来还有tcp的可以使用tcp 代 ...

  6. [转帖]nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件

    nginx学习,看这一篇就够了:下载.安装.使用:正向代理.反向代理.负载均衡.常用命令和配置文件 2019-10-09 15:53:47 冯insist 阅读数 7285 文章标签: nginx学习 ...

  7. [转帖]Nginx rewrite 规则 与 proxy_pass 实现

    Nginx rewrite 规则 与 proxy_pass 实现 https://www.cnblogs.com/jicki/p/5546916.html Nginx rewrite 规则  与 pr ...

  8. [转帖]nginx location配置详细解释

    nginx location配置详细解释 http://outofmemory.cn/code-snippet/742/nginx-location-configuration-xiangxi-exp ...

  9. [转帖]Nginx rewrite模块深入浅出详解

    Nginx rewrite模块深入浅出详解 https://www.cnblogs.com/beyang/p/7832460.html rewrite模块(ngx_http_rewrite_modul ...

  10. [转帖]nginx服务器安装及配置文件详解

    nginx服务器安装及配置文件详解 http://seanlook.com/2015/05/17/nginx-install-and-config/  发表于 2015-05-17 |  更新于: 2 ...

随机推荐

  1. flutter中全局与单页面背景图片(动态图片)

    单页面设置背景图片 使用 Container 组件和 decoration 属性: 优点:简单易用,适用于大多数情况下的页面背景设置. 缺点:无法控制背景图片的位置和层级. class MyBook ...

  2. 常见的Java中SQL注解的用法

    @Select:用于查询操作,标注在方法上,指定相应的SQL查询语句. @Select("SELECT * FROM table_name WHERE condition") Li ...

  3. 如何将没有复制或移动构造函数的对象放入vector容器

    正文 直接说答案,这个问题无法实现.原因是因为std::vector容器的插入一定会调用类对象的构造函数或者移动构造函数. 说一下为什么会有这个问题,因为不想用指针,我想直接通过类对象本身的RAII机 ...

  4. 抖音上超好听的神曲音乐,Python教你一次性下载

    不知道什么时候开始,中国出现了南抖音.北快手的互文格局(东市买骏马,西市买鞍鞯-).刚才提到了,之前比较喜欢刷抖音,对于我这种佛系程序猿,看网上这些整容妹子基本一个样.喜欢抖音主要是两个初衷,学做菜听 ...

  5. 超酷! Atlas给黑白视频“上色”

    摘要:随着人工智能技术发展,AI已经能够为黑白的老视频 "上色",重现昔日的情景,让黑白图像变得栩栩如生. 怎么样,是不是看起来题目跟昨天的博客名称差不多?昨天是图片,今天是视频. ...

  6. 再获殊荣!华为云GaussDB喜提“科技进步一等奖”

    摘要:近日,中国电子学会科学技术奖励大会颁发了2021-2022年度中国电子学会科学技术奖获奖项目,华为云主导的"GaussDB智能云原生分布式数据库"项目荣获"科技进步 ...

  7. Tarjan:这个算法大神

    摘要:图的算法是进行静态分析的基础数据算法,如何提高图的分析效率,就需要对图的算法有进一步的认识. 1. 引言 在静态分析技术中, 我们常用会将代码转成抽象语法树(AST), 然后采用深度遍历(DFS ...

  8. 在线一键生成安卓证书keystore 文件

    在线一键生成安卓证书 keystore 文件 一般的打包工具都会有默认的安卓证书提供,但如果你需要上架需要用自己申请安卓证书 keystore 文件打包 apk 现有方便方便的工具,直接在网页就可以申 ...

  9. Axure 辅助线--栅格化布局

    全局辅助线 在所有页面都会显示,比如主页面是框架.子页面通过[内联框架]去加载,为了子页面的元件不偏移,可以创建创建全局辅助线 页面辅助线

  10. Kubernetes(K8S) 监控 Prometheus + Grafana

    监控指标 集群监控 节点资源利用率 节点数 运行Pods Pod 监控 容器指标 应用程序 Prometheus 开源的 监控.报警.数据库 以HTTP协议周期性抓取被监控组件状态 不需要复杂的集成过 ...