[转帖]优化超大 Nginx 配置导致的内存碎片
https://blog.openresty.com.cn/cn/ngx-cycle-pool-frag/?src=org_news
我们最近使用 OpenResty XRay 帮助一个销售 CDN 和流量网关服务的企业客户优化了他们的 OpenResty/Nginx 服务器的内存使用。这个客户在他们的 OpenResty/Nginx 配置文件中定义了许多虚拟服务器和 URI location。OpenResty XRay 在客户的生产环境中自动进行了大部分分析,基于分析结果给出的方案让 nginx 进程的内存占用减少了大约 30%。

和我们的 OpenResty Edge 的 nginx worker 进程相比显示, 进一步的优化将会继续减少约 90%。

OpenResty XRay 是一个动态追踪产品,它可以自动分析正在运行中的应用程序,以排除性能问题、行为问题和安全漏洞,并提供可行的建议。在底层实现上,OpenResty XRay 由我们的 Y 语言驱动,可以在不同环境下支持多种不同的运行时,如 Stap+, eBPF+, GDB 和 ODB。
挑战
这个 CDN 供应商使用一个超大的“nginx.conf”配置文件来为他们的 OpenResty 服务器中的近万个虚拟主机服务。每个 nginx 主进程在启动后占用了好几个 G 的内存,在一次或多次 HUP reload 后,内存几乎翻倍。从下面 OpenResty XRay 生成的图中可以看出,最大内存占用约为 4.60GB。
我们可以从 OpenResty XRay 的应用层面内存使用明细表中看到,Glibc 分配器占用了大部分常驻内存,有 4.55GB。

而 OpenResty XRay 发现 Nginx cycle pool 占用了大量的内存:

当 Nginx 加载配置文件时,我们都知道它为这个 cycle pool 内的配置数据分配了数据结构。虽然庞大到有 1.62GB,但远远小于上面提到的 4.60GB。
RAM 依然是昂贵和稀缺的硬件资源,特别是在 AWS 和 GCP 这样的公有云上。客户希望通过降级到内存较小的机器来节约成本。
分析
OpenResty XRay 对客户的在线进程进行了深入分析。它不需要客户的应用程序进行任何协作。
- 没有额外的插件、模块或库。
- 没有代码注入或补丁。
- 没有特殊的编译或启动选项。
- 甚至不需要重新启动应用程序进程。
分析完全是以“事后”的方式进行的。多亏了 Openresty XRay 采用的动态追踪技术。
太多的空闲区块
OpenResty XRay 用 Glibc 内存分配器的分析器自动对在线 nginx 进程进行采样。分析器生成了以下柱状图,显示了分配器管理的空闲块的大小是如何分布的。

Glibc 分配器通常不会立即释放空闲块给操作系统(OS)。它可能会保留一些空闲块,以加快后续的分配速度。但有意保留的通常很小,不可能到上 G 字节。这里我们看到空闲块的大小累计已经达到 2.3GB。因此,更常见的原因是内存碎片。
查看普通堆中的内存碎片问题
大多数小的内存分配通过 brk Linux 系统调用,发生在“普通堆”中。这个堆就像一个线性的 “堆“,只能通过移动其”顶部"指针来增加或减少。在堆中间的所有空闲块不能被释放给操作系统。直到它们上面的所有块也变成空闲,它们才会被释放。
OpenResty XRay 的内存分析器可以帮助我们查看这种堆的状态。请看下面的堆图,它是在 nginx 主进程响应 HUP 信号加载新配置后的采样图。

我们可以看到,堆是向上增长的,也就是说,向高位内存地址增长。注意 brk top 指针,这是唯一可以移动的东西。绿色框属于 Nginx 的新 “cycle pool“,而粉色框属于旧 ”cycle pool”。一个有趣的现象是,Nginx 会保留旧的 cycle pool 或旧的配置数据,直到新的 cycle pool 被成功加载。这种行为是由于 Nginx 的保护机制,当新的配置加载失败时,会优雅地退回到旧的配置。不幸的是,正如我们在上面看到的,旧的配置数据的盒子(绿色)在新的数据(粉色)下面,因此只有当新的配置数据也被释放后,它们才能释放到操作系统。
事实上,在 Nginx 释放了旧的配置数据和旧的 cycle pool 后,它们原来的位置变成了空闲块,被卡在新的 cycle pool 的块下面。

这是一个教科书式的内存碎片化的例子。普通堆只能在顶部释放内存;因此,它比其他内存分配机制,如 mmap 系统调用,更容易受到内存碎片的影响。但是,mmap 会在这里拯救我们吗?不一定。
mmap 的世界
Glibc 分配器也可以通过 mmap 系统调用来分配内存。这些系统调用分配离散的内存块或内存段,这些内存块或内存段可能位于进程地址空间的几乎任何地址,并跨越任何数量的内存页。
这听起来是一个缓解上述内存碎片问题的好方法。但是当我们有意阻断普通堆的增长方式时,根据 OpenResty XRay 的分析器所产生的图表,类似程度的内存碎片仍然发生。

当应用程序(这里是 Nginx)请求分配较小内存块的时候,Glibc 倾向于分配相对较大的内存段,这里是 1MB。因此,内存碎片仍然会发生在这些 1MB 的 mmap 段内。如果一个小内存块仍在使用,那么整个内存段就不会被释放给操作系统。
在上图中,我们可以看到旧的 cycle pool 块(粉红色)和新的 cycle pool 块(绿色)仍然在许多 mmap 段中交错。
解决方案
我们为客户提出了几个解决方案。
简单方式
最简单的方式是直接解决内存碎片的问题。根据上面我们使用 OpenResty XRay 做的分析,我们应该做以下一个或多个变动。
- 避免在“普通堆”中分配 cycle pool 内存(即取消这种分配的
brk系统调用)。 - 要求 Glibc 使用适当的 mmap 段内存大小(不要太大!)来满足 cycle pool 的内存分配请求。
- 将不同 cycle pool 的内存块干净地分离到不同的 mmap 段中。
我们为 OpenResty XRay 的付费客户提供详细的优化说明。因此根本就不需要编码。
更好的方式
是的,还有一个更好的方式。开源的 OpenResty 软件提供了 Lua APIs 和 Nginx 配置指令,以动态加载(和卸载)Lua 层面的新的配置数据,而不需要通过 Nginx 配置文件机制。这使得使用一个小的恒定大小的内存来处理更多的虚拟 server 和 location 的配置数据成为可能。同时,Nginx 服务器的启动和重新加载时间也大大缩短(从很多秒到几乎为零)。事实上,有了动态配置加载,HUP reload 操作本身变得非常罕见。这种方式的一个缺点是,这需要在我们用户侧进行一些额外的 Lua 编码。
我们的 OpenResty Edge 软件产品以 OpenResty 作者所设想的最佳方式实现了这种动态配置加载和卸载。它不需要用户进行任何编码。所以这也是一个容易的选项。
结果
这位客户决定先尝试简单的方式,结果在几次 HUP reload 后,总的内存占用减少了 30%。

仍然有一些剩余的片段值得进一步关注。但我们的客户已经很满意了。此外,上面提到的更好的方式可以节省超过 90% 的总内存占用(就像在我们的 OpenResty Edge 产品中一样):

关于作者
章亦春是开源 OpenResty 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。
章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl 等,并编写过 60 多个开源软件库。
关注我们
如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。
我们也在 B 站上也有 OpenResty 官方的视频分享空间,欢迎订阅。
同时欢迎扫码关注我们的微信公众号:
[转帖]优化超大 Nginx 配置导致的内存碎片的更多相关文章
- Nginx使用教程(三):Nginx配置性能优化之I/O和TCP配置
配置Nginx I/O <br\> Sendfile 当应用程序传输文件时,内核首先缓冲数据,然后将数据发送到应用程序缓冲区. 应用程序反过来将数据发送到目的地. Sendfile方法是一 ...
- [转帖]nginx配置ssl加密(单/双向认证、部分https)
nginx配置ssl加密(单/双向认证.部分https) https://segmentfault.com/a/1190000002866627 nginx下配置ssl本来是很简单的,无论是去认证 ...
- Nginx服务器性能优化与安全配置实践指南
转载自:https://www.bilibili.com/read/cv16151784?spm_id_from=333.999.0.0 1.引言 1.1 目的 为了更好的指导部署与测试艺术升系统ng ...
- Nginx配置性能优化
大多数的Nginx安装指南告诉你如下基础知识--通过apt-get安装,修改这里或那里的几行配置,好了,你已经有了一个Web服务器了.而且,在大多数情况下,一个常规安装的nginx对你的网站来说已经能 ...
- nginx 配置优化的几个参数
nginx 配置优化的几个参数 2011-04-22 本文地址: http://blog.phpbean.com/a.cn/7/ --水平有限欢迎指正-- -- 最近在服务器上搞了一些nginx 研究 ...
- Nginx配置性能优化(转)
大多数的Nginx安装指南告诉你如下基础知识——通过apt-get安装,修改这里或那里的几行配置,好了,你已经有了一个Web服务器了.而且,在大多数情况下,一个常规安装的nginx对你的网站来说已经能 ...
- Nginx配置性能优化与压力测试webbench【转】
这一篇我们来说Nginx配置性能优化与压力测试webbench. 基本的 (优化过的)配置 我们将修改的唯一文件是nginx.conf,其中包含Nginx不同模块的所有设置.你应该能够在服务器的/et ...
- Nginx配置不当可能导致的安全问题
Nginx配置不当可能导致的安全问题 Auther: Spark1e目前很多网站使用了nginx或者tenginx(淘宝基于Nginx研发的web服务器)来做反向代理和静态服务器,ningx的配置文件 ...
- 关于Nginx配置性能优化
基本的 (优化过的)配置 将修改的唯一文件是nginx.conf,其中包含Nginx不同模块的所有设置.在服务器的/etc/nginx目录中找到nginx.conf. 首先,我们将谈论一些全局设置,然 ...
- Nginx配置优化参考
Nginx配置优化参考 ...
随机推荐
- Provider 四种消费者
Provider.of Provider.of 方法是 Provider 库中最常用的获取共享数据的方法之一.它接收一个 BuildContext 对象和一个泛型类型参数 T,会查找 Widget 树 ...
- 格网DEM生成不规则三角网TIN
目录 概述 详论 1️⃣数据准备 2️⃣转换算法 3️⃣TIN构建 4️⃣具体实现 5️⃣实验结果 参考 概述 在GIS(地理信息科学)中,地形有两种表达方式,一种是格网DEM,一种是不规则三角网TI ...
- 玩转云上数据湖,解析Serverless 技术落地
导读: 本文主要介绍Serverless计算相关技术与其在华为云数据湖探索服务(后文简称DLI)中的技术落地.Serverless是DLI将计算能力服务化和产品化关键技术,与传统IAAS和PAAS技术 ...
- 实战解析丨如何对Mysql连接请求的tcpdump内容进行分析
摘要:tcpdump是通过拦截发送和收到的网络连接中的TCP/IP和其他数据包,通过tcpdump工具帮助我们分析三次握手或者四次挥手的数据包情况,就能很容易的帮助我们分析出网络在哪一个步骤出的问题. ...
- 什么是VXLAN?为什么需要VXLAN?
摘要:本文介绍了什么是VXLAN,以及VXLAN的基本概念和工作原理,包括:为什么需要VXLAN?VXLAN与VLAN之间有啥不同?什么是VTEP?什么是VNI?VXLAN报文是如何封装的?VXLAN ...
- 跟我学ModelArts丨探索ModelArts平台个性化联邦学习API
摘要:ModelArts提供了一个实现个性化联邦学习的API--pytorch_fedamp_emnist_classification,它主要是让拥有相似数据分布的客户进行更多合作的一个横向联邦学习 ...
- AppCube视角浅析: 艾瑞咨询《2022年中国低代码行业研究报告》
摘要:近日,艾瑞咨询发布了<2022年中国低代码行业研究报告>,报告从企业数字化发展背景.低代码的发展路径.应用渗透.市场规模等方面进行深入研究分析,并洞察了发展趋势. 本文分享自华为云社 ...
- 如何利用CANN DVPP进行图片的等比例缩放?
摘要:介绍如何用昇腾AI处理器上的DVPP单元进行,图像的等比例缩放,保证图像不变形. 本文分享自华为云社区<CANN DVPP进行图片的等比例缩放>,作者:马城林 . 1. 为什么需要进 ...
- THOR:MindSpore 自研高阶优化器源码分析和实践应用
摘要:这篇文章跟大家分享下THOR的实践应用.THOR算法的部分内容当前已经在MindSpore中开源 本文分享自华为云社区<MindSpore 自研高阶优化器源码分析和实践应用>,原文作 ...
- HIVE报错 need to specify partition columns because the destination table is partitioned
解决 分区需要指定分区 insert into table XXX partition(分区='')