一台服务器报警了,内存占用过高,奇怪的是集群里其它的服务器都没问题。不过从以往的经验来看:每一个匪夷所思的问题背后,都隐藏着一个啼笑皆非的答案。
首先通过「free -m」确认一下内存情况,发现用掉了 6893M,还剩 976M:
free
然后通过「top」查看一下哪些进程占用内存多,通过「shift + m」按内存排序:
top
虽然通过 free 命令我们能确认系统可用内存不足,但是通过 top 命令我们却没有发现有内存占用大户的进程,那么内存到底都去哪里了呢?
开头我们提到过,集群里只有一台服务器有问题,其它服务器皆正常,于是我们比较了一下问题服务器和正常服务器的进程列表,结果发现问题服务器多了几个进程:
/usr/local/bin/php artisan queue:listen
/usr/local/bin/php artisan queue:work
经过确认,它们是 Laravel 队列,虽然直觉告诉我们问题与其有关联,但是进程本身并没有占用多少内存,在不能立刻确诊原因的情况下,我们用排除法把队列换到另外一台正常的服务器上看看会不会重现问题,过了一会,果然再次出现同样问题。
既然 free,top 之类的命令不能确认内存的去向,那么我们不妨看看「meminfo」:
meminfo
如上图所示,大量内存被 Slab 消耗了,更进一步讲是被 SReclaimable 消耗了,也就是说内存被一些可回收的 Slab 消耗了,更进一步的信息可以通过「slabtop」获取:
slabtop
大量内存被 dentry 消耗了,如果你也跟我一样,搞不清楚它意味着什么,搜索吧,能翻墙用 Google,不能翻墙用 AOL,反正别用百度,我找到如下介绍:
-
Linux服务器Cache占用过多内存导致系统内存不足问题的排查解决
-
Linux服务器Cache占用过多内存导致系统内存不足问题的排查解决(续)
简而言之,内存 dentry 里缓存了最近访问过的文件信息,如果频繁的操作大量文件,那么 dentry 就会不断的增加,于是问题就变为确认 Laravel 队列有没有类似问题。
前面提到过,Laravel 队列有一个 listen 进程,还有一个 work 进程,从名字我们就能判断出来,前者是主进程,后者是子进程,子进程是干活的进程,可是当我直接 strace 跟踪子进程的时候,却提示我子进程不存在,进一步调试发现,原来子进程会不断重启!
既然我们不好直接跟踪子进程,那么不妨从父进程入手跟踪子进程的文件操作:
shell> strace -f -e trace=open,close,stat,unlink -p $( ps aux | grep "[q]ueue:listen" | awk '{print $2}')
可惜 Laravel 本身号称是巨匠框架,依赖一坨一坨的文件,所以跟踪结果里充斥着大量框架文件本身正常的 open,close,stat 操作,改为只跟踪 unlik 操作试试:
shell> strace -f -e trace=unlink -p $( ps aux | grep "[q]ueue:listen" | awk '{print $2}')
发现 Laravel 队列频繁的执行删除文件操作,每重启一次子进程就执行一次删除:
unlink(“/tmp/.ZendSem.aXaa3Z”)
unlink(“/tmp/.ZendSem.teQG0Y”)
unlink(“/tmp/.ZendSem.Bn3ien”)
unlink(“/tmp/.ZendSem.V4s8RX”)
unlink(“/tmp/.ZendSem.PnNuTN”)
因为临时文件的名字各不相同,所以消耗了大量的 dentry 缓存。查阅 Laravel 队列的 文档 ,发现 Laravel 队列实际上也提供了不重启的守护进程模式,这样就不会频繁创建大量临时文件,进而也就不会消耗大量的 dentry 缓存,推荐使用。
如果频繁创建大量临时文件的情况无法避免,那么按照 Linux 文档 的描述,我们可以通过设置 drop_caches 为 2 来删除可回收的 slab(包括 dentries 和 inodes),较粗野:
shell> echo 2 > /proc/sys/vm/drop_caches
此外还可以通过设置 vfs_cache_pressure 大于 100 来增加回收概率,较温柔:
shell> echo 1000 > /proc/sys/vm/vfs_cache_pressure
从测试结果看,vfs_cache_pressure 的作用有限,当然也可能是我姿势不对。还有一些资料说 min_free_kbytes 也可以解决此类问题,不过鉴于这个参数有一定危险性,我建议大家别乱改,这里就不多说了,有兴趣的可以自行查阅相关资料。

DependencyInjection(DI)inPHPenhancescodeflexibilityandtestabilitybydecouplingdependencycreationfromusage.ToimplementDIeffectively:1)UseDIcontainersjudiciouslytoavoidover-engineering.2)Avoidconstructoroverloadbylimitingdependenciestothreeorfour.3)Adhe

到Improveyourphpwebsite的实力,UsEthestertate:1)emplastOpCodeCachingWithOpcachetCachetOspeedUpScriptInterpretation.2)优化的atabasequesquesquesquelies berselectingOnlynlynnellynnessaryfields.3)usecachingsystemssslikeremememememcachedisemcachedtoredtoredtoredsatabaseloadch.4)

是的,itispossibletosendMassemailswithp.1)uselibrarieslikeLikePhpMailerorSwiftMailerForeffitedEmailSending.2)enasledeLaysBetemailStoavoidSpamflagssspamflags.3)sylectynamicContentToimpovereveragement.4)

DependencyInjection(DI)inPHPisadesignpatternthatachievesInversionofControl(IoC)byallowingdependenciestobeinjectedintoclasses,enhancingmodularity,testability,andflexibility.DIdecouplesclassesfromspecificimplementations,makingcodemoremanageableandadapt

使用PHP发送电子邮件的最佳方法包括:1.使用PHP的mail()函数进行基本发送;2.使用PHPMailer库发送更复杂的HTML邮件;3.使用SendGrid等事务性邮件服务提高可靠性和分析能力。通过这些方法,可以确保邮件不仅到达收件箱,还能吸引收件人。

计算PHP多维数组的元素总数可以使用递归或迭代方法。1.递归方法通过遍历数组并递归处理嵌套数组来计数。2.迭代方法使用栈来模拟递归,避免深度问题。3.array_walk_recursive函数也能实现,但需手动计数。

在PHP中,do-while循环的特点是保证循环体至少执行一次,然后再根据条件决定是否继续循环。1)它在条件检查之前执行循环体,适合需要确保操作至少执行一次的场景,如用户输入验证和菜单系统。2)然而,do-while循环的语法可能导致新手困惑,且可能增加不必要的性能开销。

在PHP中高效地哈希字符串可以使用以下方法:1.使用md5函数进行快速哈希,但不适合密码存储。2.使用sha256函数提高安全性。3.使用password_hash函数处理密码,提供最高安全性和便捷性。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

SublimeText3汉化版
中文版,非常好用

Dreamweaver CS6
视觉化网页开发工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境