Heim > Artikel > Betrieb und Instandhaltung > Erkunden Sie die Nutzung physischer Speicherressourcen (oder RAM) im gemeinsam genutzten Speicherbereich von OpenResty und Nginx?
OpenResty- und Nginx-Server sind normalerweise mit gemeinsam genutzten Speicherbereichen konfiguriert, um Daten zu speichern, die von allen Arbeitsprozessen gemeinsam genutzt werden. Beispielsweise verwenden die Nginx-Standardmodule ngx_http_limit_req und ngx_http_limit_conn gemeinsam genutzte Speicherbereiche zum Speichern von Statusdaten, um die Benutzeranforderungsrate und die Parallelität von Benutzeranforderungen über alle Arbeitsprozesse hinweg zu begrenzen. Das ngx_lua-Modul von OpenResty bietet über lua_shared_dict eine gemeinsam genutzte speicherbasierte Datenwörterbuchspeicherung für Benutzer-Lua-Code.
In diesem Artikel wird anhand mehrerer einfacher und unabhängiger Beispiele untersucht, wie diese gemeinsam genutzten Speicherbereiche physische Speicherressourcen (oder RAM) nutzen. Wir werden auch die Auswirkungen der Shared-Memory-Nutzung auf Prozessspeichermetriken auf Systemebene wie VSZ
und RSSps
/ untersuchen. Code> und andere Indikatoren. ps
等系统工具的结果中的 VSZ
和 RSS
等指标。
与本博客网站 中的几乎所有技术类文章类似,我们使用 OpenResty XRay 这款动态追踪产品对未经修改的 OpenResty 或 Nginx 服务器和应用的内部进行深度分析和可视化呈现。因为 OpenResty XRay 是一个非侵入性的分析平台,所以我们不需要对 OpenResty 或 Nginx 的目标进程做任何修改 -- 不需要代码注入,也不需要在目标进程中加载特殊插件或模块。这样可以保证我们通过 OpenResty XRay 分析工具所看到的目标进程内部状态,与没有观察者时的状态是完全一致的。
我们将在多数示例中使用 ngx_lua 模块的 lua_shared_dict,因为该模块可以使用自定义的 Lua 代码进行编程。我们在这些示例中展示的行为和问题,也同样适用于所有标准 Nginx 模块和第三方模块中的其他共享内存区。
Nginx 及其模块通常使用 Nginx 核心里的 slab 分配器 来管理共享内存区内的空间。这个slab 分配器专门用于在固定大小的内存区内分配和释放较小的内存块。
在 slab 的基础之上,共享内存区会引入更高层面的数据结构,例如红黑树和链表等等。
slab 可能小至几个字节,也可能大至跨越多个内存页。
操作系统以内存页为单位来管理进程的共享内存(或其他种类的内存)。
在 x86_64
Linux 系统中,默认的内存页大小通常是 4 KB,但具体大小取决于体系结构和 Linux 内核的配置。例如,某些 Aarch64
Ebenso Wir verwenden OpenResty XRay
dieses Dynamic Tracing🎜Das Produkt geht tief in die Interna von unveränderten OpenResty- oder Nginx-Servern und -Anwendungen ein. Analyse und visuelle Präsentation . Denn OpenResty XRay🎜 ist eine nicht-invasive Analyseplattform, die wir also nicht benötigen Es werden keine Änderungen am Zielprozess von OpenResty oder Nginx vorgenommen – es ist keine Code-Injektion erforderlich und es müssen keine speziellen Plugins oder Module in den Zielprozess geladen werden. Dadurch wird sichergestellt, dass wir den internen Status des Zielprozesses über OpenResty XRay🎜 sehen können Analysetool, das ohne Beobachter vollständig mit dem Zustand übereinstimmt. 🎜🎜Wir werden in den meisten Beispielen das 🎜lua_shared_dict🎜 des 🎜ngx_lua🎜-Moduls verwenden, da dieses Modul mit benutzerdefiniertem Lua-Code programmiert werden kann. Das Verhalten und die Probleme, die wir in diesen Beispielen demonstrieren, gelten auch für andere gemeinsam genutzte Speicherbereiche in allen Standard-Nginx-Modulen und Modulen von Drittanbietern. 🎜x86_64
-Linux-Systemen beträgt die Standardspeicherseitengröße normalerweise 4 KB, die genaue Größe hängt jedoch von der Architektur und der Linux-Kernelkonfiguration ab. Einige Aarch64
-Linux-Systeme haben beispielsweise Speicherseitengrößen von bis zu 64 KB. 🎜🎜Wir werden die Details des gemeinsam genutzten Speicherbereichs der OpenResty- und Nginx-Prozesse auf Speicherseitenebene bzw. Slab-Ebene sehen. 🎜Im Gegensatz zu Ressourcen wie Festplatten ist physischer Speicher (oder RAM) immer eine sehr wertvolle Ressource.
Die meisten modernen Betriebssysteme haben eine Optimierungstechnologie namens Demand Paging (Demand-Paging) implementiert, die verwendet wird, um den Druck auf RAM-Ressourcen durch Benutzeranwendungen zu verringern. Insbesondere wenn Sie einen großen Speicherblock zuweisen, verschiebt der Betriebssystemkern die tatsächliche Zuweisung von RAM-Ressourcen (oder physischen Speicherseiten), bis die Daten auf der Speicherseite tatsächlich verwendet werden. Wenn einem Benutzerprozess beispielsweise 10 Speicherseiten zugewiesen werden, er jedoch nur 3 Speicherseiten verwendet, hat das Betriebssystem möglicherweise nur diese 3 Speicherseiten dem RAM-Gerät zugeordnet. Dieses Verhalten gilt auch für gemeinsam genutzte Speicherbereiche, die in Nginx- oder OpenResty-Anwendungen zugewiesen werden. Der Benutzer kann in der Datei nginx.conf
einen riesigen gemeinsam genutzten Speicherbereich konfigurieren, stellt jedoch möglicherweise fest, dass nach dem Start des Servers fast kein zusätzlicher Speicher belegt ist Beim ersten Start werden tatsächlich Shared-Memory-Seiten verwendet. nginx.conf
文件中配置庞大的共享内存区,但他可能会注意到在服务器启动之后,几乎没有额外占用多少内存,毕竟通常在刚启动的时候,几乎没有共享内存页被实际使用到。
我们以下面这个 nginx.conf
文件为例。该文件分配了一个空的共享内存区,并且从没有使用过它:
master_process on; worker_processes 2; events { worker_connections 1024; } http { lua_shared_dict dogs 100m; server { listen 8080; location = /t { return 200 "hello world\n"; } } }
我们通过 lua_shared_dict 指令配置了一个 100 MB 的共享内存区,名为 dogs
。并且我们为这个服务器配置了 2 个工作进程。请注意,我们在配置里从没有触及这个 dogs
区,所以这个区是空的。
可以通过下列命令启动这个服务器:
mkdir ~/work/ cd ~/work/ mkdir logs/ conf/ vim conf/nginx.conf # paste the nginx.conf sample above here /usr/local/openresty/nginx/sbin/nginx -p $PWD/
然后用下列命令查看 nginx 进程是否已在运行:
$ ps aux|head -n1; ps aux|grep nginx USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND agentzh 9359 0.0 0.0 137508 1576 ? Ss 09:10 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -p /home/agentzh/work/ agentzh 9360 0.0 0.0 137968 1924 ? S 09:10 0:00 nginx: worker process agentzh 9361 0.0 0.0 137968 1920 ? S 09:10 0:00 nginx: worker process
这两个工作进程占用的内存大小很接近。下面我们重点研究 PID 为 9360 的这个工作进程。在 OpenResty XRay 控制台的 Web 图形界面中,我们可以看到这个进程一共占用了 134.73 MB 的虚拟内存(virtual memory)和 1.88 MB 的常驻内存(resident memory),这与上文中的 ps
命令输出的结果完全相同:
正如我们的另一篇文章 《OpenResty 和 Nginx 如何分配和管理内存》中所介绍的,我们最关心的就是常驻内存的使用量。常驻内存将硬件资源实际映射到相应的内存页(如 RAM 1)。所以我们从图中看到,实际映射到硬件资源的内存量很少,总计只有 1.88MB。上文配置的 100 MB 的共享内存区在这个常驻内存当中只占很小的一部分(详情请见后续的讨论)。
当然,共享内存区的这 100 MB 还是全部贡献到了该进程的虚拟内存总量中去了。操作系统会为这个共享内存区预留出虚拟内存的地址空间,不过,这只是一种簿记记录,此时并不占用任何的 RAM 资源或其他硬件资源。
空 不是 空无一物
我们可以通过该进程的“应用层面的内存使用量的分类明细”图,来检查空的共享内存区是否占用了常驻(或物理)内存。
有趣的是,我们在这个图中看到了一个非零的 Nginx Shm Loaded
(已加载的 Nginx 共享内存)组分。这部分很小,只有 612 KB,但还是出现了。所以空的共享内存区也并非空无一物。这是因为 Nginx 已经在新初始化的共享内存区域中放置了一些元数据,用于簿记目的。这些元数据为 Nginx 的 slab 分配器所使用。
已加载和未加载内存页
我们可以通过 OpenResty XRay 自动生成的下列图表,查看共享内存区内被实际使用(或加载)的内存页数量。
我们发现在dogs
区域中已经加载(或实际使用)的内存大小为 608 KB,同时有一个特殊的 ngx_accept_mutex_ptr
被 Nginx 核心自动分配用于 accept_mutex 功能。
这两部分内存的大小相加为 612 KB,正是上文的饼状图中显示的 Nginx Shm Loaded
nginx.conf
-Datei als Beispiel. Die Datei weist einen leeren gemeinsamen Speicherbereich zu und verwendet ihn nie: 🎜init_by_lua_block { for i = 1, 300000 do ngx.shared.dogs:set("key" .. i, i) end }🎜Wir übergeben lua_shared_dict🎜 konfiguriert einen 100 MB großen gemeinsam genutzten Speicherbereich mit dem Namen
dogs
. Und wir haben zwei Worker-Prozesse für diesen Server konfiguriert. Bitte beachten Sie, dass wir den Abschnitt dogs
in der Konfiguration nie berühren, er ist also leer. 🎜🎜Sie können diesen Server mit dem folgenden Befehl starten: 🎜kill -QUIT `cat logs/nginx.pid` /usr/local/openresty/nginx/sbin/nginx -p $PWD/🎜 Anschließend überprüfen Sie mit dem folgenden Befehl, ob der Nginx-Prozess ausgeführt wird: 🎜
$ ps aux|head -n1; ps aux|grep nginx USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND agentzh 29733 0.0 0.0 137508 1420 ? Ss 13:50 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -p /home/agentzh/work/ agentzh 29734 32.0 0.5 138544 41168 ? S 13:50 0:00 nginx: worker process agentzh 29735 32.0 0.5 138544 41044 ? S 13:50 0:00 nginx: worker process🎜Die von diesen beiden Worker-Prozessen belegte Speichergröße liegt sehr nahe beieinander. Im Folgenden konzentrieren wir uns auf den Arbeitsprozess mit PID 9360. Wir können diesen Prozess in der grafischen Weboberfläche der OpenResty XRay🎜-Konsole sehen 134,73 MB virtueller Speicher und 1,88 MB residenter Speicher sind belegt, was genau dem Ergebnis entspricht, das durch den Befehl
ps
oben ausgegeben wird: 🎜🎜🎜🎜Wie unser anderer Artikel "Wie OpenResty und Nginx Speicher zuweisen und verwalten" 🎜Einführung: Was uns am meisten Sorgen bereitet, ist die Nutzung des residenten Speichers. Der residente Speicher ordnet Hardwareressourcen tatsächlich den entsprechenden Speicherseiten zu (z. B. RAM 1🎜 ). Auf dem Bild sehen wir also, dass die tatsächlich den Hardwareressourcen zugeordnete Speichermenge sehr gering ist, insgesamt nur 1,88 MB. Der oben konfigurierte 100-MB-Shared-Memory-Bereich macht nur einen kleinen Teil dieses residenten Speichers aus (Einzelheiten siehe nachfolgende Diskussion). 🎜🎜Natürlich werden die 100 MB des gemeinsam genutzten Speicherbereichs immer noch zum gesamten virtuellen Speicher des Prozesses beigetragen. Das Betriebssystem reserviert einen virtuellen Speicheradressraum für diesen gemeinsam genutzten Speicherbereich. Dies ist jedoch nur eine Buchhaltungsaufzeichnung und belegt zu diesem Zeitpunkt keine RAM-Ressourcen oder andere Hardwareressourcen. 🎜🎜🎜Leerist nicht nichts 🎜🎜🎜Wir können die „Speichernutzung der Prozessklassifizierung auf Anwendungsebene“ übergeben. Diagramm, um zu überprüfen, ob der leere gemeinsam genutzte Speicherbereich residenten (oder physischen) Speicher belegt. 🎜🎜🎜🎜Interessant Interessant ist, dass wir in dieser Abbildung eine Nginx Shm Loaded
-Komponente (geladener gemeinsamer Nginx-Speicher) ungleich Null sehen. Der Teil ist klein, nur 612 KB, wird aber trotzdem angezeigt. Der leere gemeinsam genutzte Speicherbereich ist also nicht leer. Dies liegt daran, dass Nginx zu Buchhaltungszwecken einige Metadaten im neu initialisierten gemeinsam genutzten Speicherbereich abgelegt hat. Diese Metadaten werden vom Slab Allocator von Nginx verwendet. 🎜🎜🎜Geladene und entladene Speicherseiten🎜🎜🎜Wir können OpenResty XRay🎜 generiert automatisch das folgende Diagramm, um die Anzahl der tatsächlich verwendeten (oder geladenen) Speicherseiten im gemeinsam genutzten Speicherbereich anzuzeigen. 🎜🎜🎜🎜uns Es wurde festgestellt, dass die Speichergröße, die im Bereich dogs
geladen (oder tatsächlich verwendet) wurde, 608 KB beträgt, und es gibt einen speziellen ngx_accept_mutex_ptr
, der automatisch von zugewiesen wird Nginx-Kern für die Funktion „accept_mutex“. 🎜🎜Die kombinierte Größe dieser beiden Teile des Speichers beträgt 612 KB, was genau der Größe von Nginx Shm Loaded
entspricht, die im obigen Kreisdiagramm gezeigt wird. 🎜如前文所述,dogs
区使用的 608 KB 内存实际上是 slab 分配器 使用的元数据。
未加载的内存页只是被保留的虚拟内存地址空间,并没有被使用过。
关于进程的页表
我们没有提及的一种复杂性是,每一个 nginx 工作进程其实都有各自的页表。CPU 硬件或操作系统内核正是通过查询这些页表来查找虚拟内存页所对应的存储。因此每个进程在不同共享内存区内可能有不同的已加载页集合,因为每个进程在运行过程中可能访问过不同的内存页集合。为了简化这里的分析,OpenResty XRay 会显示所有的为任意一个工作进程加载过的内存页,即使当前的目标工作进程从未碰触过这些内存页。也正因为这个原因,已加载内存页的总大小可能(略微)高于目标进程的常驻内存的大小。
空闲的和已使用的 slab
如上文所述,Nginx 通常使用 slabs 而不是内存页来管理共享内存区内的空间。我们可以通过 OpenResty XRay 直接查看某一个共享内存区内已使用的和空闲的(或未使用的)slabs 的统计信息:
如我们所预期的,我们这个例子里的大部分 slabs 是空闲的或未被使用的。注意,这里的内存大小的数字远小于上一节中所示的内存页层面的统计数字。这是因为 slabs 层面的抽象层次更高,并不包含 slab 分配器针对内存页的大小补齐和地址对齐的内存消耗。
我们可以通过OpenResty XRay进一步观察在这个 dogs
区域中各个 slab 的大小分布情况:
我们可以看到这个空的共享内存区里,仍然有 3 个已使用的 slab 和 157 个空闲的 slab。这些 slab 的总个数为:3 + 157 = 160个。请记住这个数字,我们会在下文中跟写入了一些用户数据的 dogs
区里的情况进行对比。
下面我们会修改之前的配置示例,在 Nginx 服务器启动时主动写入一些数据。具体做法是,我们在 nginx.conf
文件的 http {}
配置分程序块中增加下面这条 init_by_lua_block 配置指令:
init_by_lua_block { for i = 1, 300000 do ngx.shared.dogs:set("key" .. i, i) end }
这里在服务器启动的时候,主动对 dogs
共享内存区进行了初始化,写入了 300,000 个键值对。
然后运行下列的 shell 命令以重新启动服务器进程:
kill -QUIT `cat logs/nginx.pid` /usr/local/openresty/nginx/sbin/nginx -p $PWD/
新启动的 Nginx 进程如下所示:
$ ps aux|head -n1; ps aux|grep nginx USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND agentzh 29733 0.0 0.0 137508 1420 ? Ss 13:50 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -p /home/agentzh/work/ agentzh 29734 32.0 0.5 138544 41168 ? S 13:50 0:00 nginx: worker process agentzh 29735 32.0 0.5 138544 41044 ? S 13:50 0:00 nginx: worker process
虚拟内存与常驻内存
针对 Nginx 工作进程 29735,OpenResty XRay 生成了下面这张饼图:
显然,常驻内存的大小远高于之前那个空的共享区的例子,而且在总的虚拟内存大小中所占的比例也更大(29.6%)。
虚拟内存的使用量也略有增加(从 134.73 MB 增加到了 135.30 MB)。因为共享内存区本身的大小没有变化,所以共享内存区对于虚拟内存使用量的增加其实并没有影响。这里略微增大的原因是我们通过 init_by_lua_block 指令新引入了一些 Lua 代码(这部分微小的内存也同时贡献到了常驻内存中去了)。
应用层面的内存使用量明细显示,Nginx 共享内存区域的已加载内存占用了最多常驻内存:
已加载和未加载内存页
Jetzt gibt es in diesem gemeinsamen Speicherbereich von dogs
viel mehr geladene Speicherseiten, und auch die Anzahl der entladenen Speicherseiten wurde deutlich reduziert: dogs
共享内存区里,已加载的内存页多了很多,而未加载的内存页也有了相应的显著减少:
空的和已使用的 slab
现在 dogs
共享内存区增加了 300,000 个已使用的 slab(除了空的共享内存区中那 3 个总是会预分配的 slab 以外):
显然,lua_shared_dict 区中的每一个键值对,其实都直接对应一个 slab。
空闲 slab 的数量与先前在空的共享内存区中的数量是完全相同的,即 157 个 slab:
正如我们上面所演示的,共享内存区在应用实际访问其内部的内存页之前,都不会实际耗费物理内存资源。因为这个原因,用户可能会观察到 Nginx 工作进程的常驻内存大小似乎会持续地增长,特别是在进程刚启动之后。这会让用户误以为存在内存泄漏。下面这张图展示了这样的一个例子:
通过查看 OpenResty XRay 生成的应用级别的内存使用明细图,我们可以清楚地看到 Nginx 的共享内存区域其实占用了绝大部分的常驻内存空间:
这种内存增长是暂时的,会在共享内存区被填满时停止。但是当用户把共享内存区配置得特别大,大到超出当前系统中可用的物理内存的时候,仍然是有潜在风险的。正因为如此,我们应该注意观察如下所示的内存页级别的内存使用量的柱状图:
图中蓝色的部分可能最终会被进程用尽(即变为红色),而对当前系统产生冲击。
Nginx 支持通过 HUP 信号来重新加载服务器的配置而不用退出它的 master 进程(worker 进程仍然会优雅退出并重启)。通常 Nginx 共享内存区会在 HUP 重新加载(HUP reload)之后自动继承原有的数据。所以原先为已访问过的共享内存页分配的那些物理内存页也会保留下来。于是想通过 HUP 重新加载来释放共享内存区内的常驻内存空间的尝试是会失败的。用户应改用 Nginx 的重启或二进制升级操作。
值得提醒的是,某一个 Nginx 模块还是有权决定是否在 HUP 重新加载后保持原有的数据。所以可能会有例外。
我们在上文中已经解释了 Nginx 的共享内存区所占用的物理内存资源,可能远少于 nginx.conf
Leere und gebrauchte Slabs
dogs
300.000 genutzte Slabs hinzugefügt (zusätzlich zum leeren Shared-Memory-Bereich). Diese 3 werden immer vorab hinzugefügt. zugewiesen (außer der Platte):
Natürlich lua_shared_dict Jedes Schlüssel-Wert-Paar in dem Bereich entspricht tatsächlich direkt einer Platte.
🎜Die Anzahl der freien Platten entspricht genau der vorherigen Anzahl im leeren gemeinsam genutzten Speicherbereich, also 157 Platten: 🎜🎜🎜nginx.conf
Die konfigurierte Größe in der Datei. Dies ist der Funktion „Demand Paging“ in modernen Betriebssystemen zu verdanken. Wir haben gezeigt, dass einige Speicherseiten und Slabs weiterhin im leeren gemeinsam genutzten Speicherbereich verwendet werden, um Metadaten zu speichern, die vom Slab-Allokator selbst benötigt werden. Durch den erweiterten Analysator von 🎜OpenResty XRay🎜 können wir den laufenden Nginx-Worker-Prozess in Echtzeit überprüfen und den tatsächlich verwendeten oder im gemeinsam genutzten Speicherbereich geladenen Speicher anzeigen, einschließlich der beiden verschiedenen Ebenen von Speicherseiten und -platten. 🎜🎜Andererseits wird die Optimierung des On-Demand-Paging auch dazu führen, dass der Speicher für einen gewissen Zeitraum weiter wächst. Dabei handelt es sich eigentlich nicht um ein Speicherleck, es birgt jedoch dennoch ein gewisses Risiko. Wir haben auch erklärt, dass der HUP-Neuladevorgang von Nginx normalerweise nicht die vorhandenen Daten im gemeinsam genutzten Speicherbereich löscht🎜🎜Empfohlenes Tutorial: 🎜nginx-Tutorial🎜🎜
Das obige ist der detaillierte Inhalt vonErkunden Sie die Nutzung physischer Speicherressourcen (oder RAM) im gemeinsam genutzten Speicherbereich von OpenResty und Nginx?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!