LNMP高并发性能优化使AB测试RPS超过500

Song2896 次浏览0个评论2017年12月26日
首先要给大家说明以下几点: -篇幅和时间有限,本篇着重讲实战,理论部分交给超链接 -2.本人水平有限,希望大家不吝赐教 -3.现在是一个CPU过剩的时代,或者说是一个非常强悍的CPU带着它坑逼的队友们(io总线,内存,缓存,硬盘,Linux,多进程模型,脚本语言

首先要给大家说明以下几点:

  • 1.篇幅和时间有限,本篇着重讲实战,理论部分交给超链接
  • 2.本人水平有限,希望大家不吝赐教
  • 3.现在是一个CPU过剩的时代,或者说是一个非常强悍的CPU带着它坑逼的队友们(io总线,内存,缓存,硬盘,Linux,多进程模型,脚本语言,网络带宽)去超神
  • 4.默认情况下使用的是2核/4G ECS

开始着手优化之前我就明显感觉到这个问题稍稍的超出了我的能力范围,但是没关系啊,先看看大家是怎么做的。

我要处理的问题有个非常洋气的名字c10kc10k实际上瓶颈往往出在io和内文切换,参考。epoll很熟悉嘛,nginx已经做好了,咱也甭操心了。协程也甭考虑了,有本事你把fpm的代码用协程给重构了。那问题就简单多了,优化io就成为了。

LNMP高并发性能优化使AB测试RPS超过500

一、优化前奏

在优化之前务必开启以下日志:

# nginx 日志
nginx access.log error.log
# PHP 日志
php error.log
fpm error.log slow.log

开始优化之前用ab压力测试发现rps很低,这时候nginx输出日志提示连接数过多。参考基友的文章LNMP中web高并发优化配置以及配置详解 (这货钱多、活好、风骚男同志赶紧加他)做了比较基础的优化后nginx处理能力小幅上升。这时候rps依旧不是很乐观:46。敲个top命令看了下cpu才占用64%!!!为啥cpu跑不满就就到瓶颈了???这个问题很诡异啊——那是因为你不懂linuxtop命令详解)。刷新了我的三观,us不是used,而是user的意思。是user占用cpu64%的时间片。id是0也就是说cpu已经跑满了。让人感觉不可思议的是si占用了9%左右,sy占用了27%左右。

这让人感觉很不爽啊,就像是辛辛苦苦干了一个月36%的薪水都不属于自己的!

  • si:软中断占用百分比
  • sy:内核空间占用CPU百分比

很明显嘛,内文切换。这个要优化fpm模型咱就暂时不讨论了,如果你真想在这方面优化,继续往下看,下面讨论。

二、优化io&将mysql独立出去

很明显嘛,内文切换。这个要优化fpm模型咱就暂时不讨论了,如果你真想在这方面优化,继续往下看

实际上我没有做特殊的io优化,没达到那个量级嘛!我们公司现在的并发能达到1k足以。然后我就开了台io优化过的ecs+ssd。然后把mysql独立出去使用RDS+读写分离。然后rps就上升到468左右了,注意哦!还没加负载均衡。给我马哥烧柱香吧。

个人建议如果条件允许不要自己搭mysql读写分离,水很深,你买阿里云RDS的钱远远少于你请一个高级php的钱。下面我着重讲解一下为啥我非要用io优化+ssd。这就要看你对computer science有多深入的了解了。感谢我大学在cs上打下了不错的基础,然后去翻linux内核的书的时候让我可以轻松点进入状态。

三、扯点Linux内核

1.首先你需要了解的是虚拟内存

这是个很扯的问题呀,每个进程都有4G的内存空间去存对象、变量和数据之类的。而且是放在硬盘上。让人感觉更扯是内存不直接参与运算,直接参与运算的只有寄存器cpu总要把数据拉倒寄存器里把,总待有个桥之类的东西实现这个功能吧——这个东西叫io总线

2.io总线。

有限的时间我只查到了IBM PowerPCcpu一共只有655368bitio总线。现在你终于知道nginxworker_rlimit_nofile为啥要设置65535了吧,我猜测要留一个给系统中断用。这也是为啥linux的最大文件打开数是65535

千军万马过65536个独木桥。

这也是为啥会有层出不穷的io复用模型的出现。

其实后面兜了不少圈性能提升不明显。由于公司使用的是一个非常蛋疼的框架,微擎(简称we7)。多次给高层建议重构无果,最终只能硬着头皮优化了。在这里顺便提醒大家一句:千万不要不要以为php简单就动不动自己动手写个框架,因为你很难有机会像开源框架一样接受市场的考验,你更难有开源社区那么多有热情的志愿者不断贡献代码去迭代。

我简单拿we7tp5对比了一下。发现tp5在优化完nginx cgi连接超时以后表现的很稳定ab测试下几乎不会有Failed。但是we7只要超过1000的用户并发就会不断的报以下三个错误:

2017/12/25 09:37:03 [error] 22582#0: *16081 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 10.26.68.75, server: www.medianewvisual.com, request: "GET /h5/index.php?c=insurance_drawtest&d=StarDraw&i=1 HTTP/1.0", upstream: "fastcgi://127.0.0.1:9001", host: "medianewvisual.com"

2017/12/25 09:37:03 [error] 22582#0: *16081 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 10.26.68.75, server: www.medianewvisual.com, request: "GET /h5/index.php?c=insurance_drawtest&d=StarDraw&i=1 HTTP/1.0", upstream: "fastcgi://127.0.0.1:9001", host: "medianewvisual.com"

2017/12/25 09:46:31 [error] 22581#0: *233603 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 10.26.68.75, server: www.medianewvisual.com, request: "GET /h5/index.php?c=insurance_drawtest&d=StarDraw&i=1 HTTP/1.0", upstream: "fastcgi://127.0.0.1:9001", host: "medianewvisual.com"

这个其实是考验一个框架的功力。我简单看了一下we7源码,数据库连接全是胡扯,就一个pdo对象做了单例(php的单例只能做到Java单例的20%)。完全没有连接池的概念。再仔细看看ab的结果

\6237186-4a86e44193925b7b.png

这也就难怪请求越多死的越多了。更可怕的是一个错误溢出消耗的cpu资源是正常的完整请求的三倍。

3.高级优化思路

大致看了tp5的源码,tp5是有数据库连接池。但是对于没有常住内存进程的php来说,然并卵啊。相比较而言Java就做的比较好,参考c3p0数据库连接池原理。假使就想用php复用mysql连接池呢?可以使用swoole来实现。如果你仅仅做到了这一步,我觉得还不够。可以参考redispipeline设计一个缓冲池批量给mysql服务器同步数据。如果再配合上swoole的异步io就更好了。

fpm是使用进程模型做计算密集处理的。以前在看python的协程、线程和进程的时候有“io密集型用协程,计算密集型用进程”的说法。线程切换能占用近1/3cpu确实很可怕。如果fpm能用协程模型实现着实能提高很高的效率。另外一条路子个人认为也是php未来的一个趋势——jit技术。难道只有我一个人觉得既然nginx都已经做了多workerio复用、请求排队这些操作了,fpm岂不是很多余吗?未来一定会有人用php实现一个和OpenRest类似的东西。

在很大的请求量下,频繁的写入日志(nginx log)也是一个很头疼的问题。同样的nginx应该提供一种批量写入日志/延迟写入日志的技术。这点我没有验证,不知道nginx或者Tengine有没有实现类似的功能。! 转自原文:记一次LNMP优化过程

请登录后评论
    当前暂无评价,快来发表您的观点吧...
    公告提示
    本站主要用于学习交流Laravel技术,分享Laravel优秀案例;同时非常欢迎大家投稿哦!如果有招聘信息也可以联系我我帮忙发布哦!