由于某些原因我们的项目使用了NGINX代理下载请求,当NGINX的STATUS中的Writing达到500左右时,服务器的CPU就达到100%了。 项目背景 项目主要提供用户的下载,包大则有300M,小则10来M。项目需要记下载日志,会把下载时间,用户请求的大小,下载了多少,下载时的并发数,是否进行了断点续传等信息记录下来,日志记录使用的是Servlet来实现,故而引入了WEB容器Tomcat,前端加入了Nginx代理,来支持多个Tomcat,以解决单个Tomcat性能有限的问题。 问题分析 首先是确认CPU是谁引起的较高,通过使用iostat命令查看大概是这样的效果。
[java]
avg-cpu: %user %nice %system %iowait %steal %idle
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util 上述只是展示了一个示例,由于上班无法截图。大概是iowait达到90%,同时avgqu-sz达到300左右,写扇区数达到14000,但是奇怪的是svctm的时间在3ms,而且比较稳定,await的时间在500以上。由于svctm比较小,说明不是磁盘性能的问题,据说在20ms以上才不正常。总体而言应该是写请求太多了,这个结论当时并没有立刻得到。而是通过找了一下Nginx的proxy_buffer的相关资料,关掉proxy_buffer后CPU降下来得到的结论。 Nginx Proxy buffer 工作原理 Proxy buffer是Nginx在使用proxy功能是缓存后台返回数据的一种机制。通过proxy_buffering off/on;来关闭和打开缓存,默认他是打开的。proxy_buffer的更多介绍。这里来说一下关键的要点。
这样在我们这样一个下载服务器的环境中就将意味着每一次请求可能都意味着写一个临时的下载文件,若有500左右的同时下载则将意味着500个同时的请求写临时文件,这就将引起大量的IO请求,这就是造成CPU飙高的主因了。但是大量的直接下载一般不会出现CPU很高的情况呢,这是因为操作系统缓存了常用的文件,可通过下面的命令看到。
[java] b 表示阻塞的进程 swpd 虚拟内存已使用的大小,如果大于0,表示你的机器物理内存不足了,但若si、so项长期为0也说明是正常。的 free 空闲的物理内存的大小,我的机器内存总共8G,剩余3415M。 buff Linux/Unix系统是用来存储,目录里面有什么内容,权限等的缓存,我本机大概占用300多M cache cache直接用来记忆我们打开的文件,给文件做缓冲,我本机大概占用300多M(这里是Linux/Unix的聪明之处,把空闲的物理内存的一部分拿来做文件和目录的缓存,是为了提高 程序执行的性能,当程序使用内存时,buffer/cached会很快地被使用。) si 每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。我的机器内存充裕,一切正常。 so 每秒虚拟内存写入磁盘的大小,如果这个值大于0,同上。 bi 块设备每秒接收的块数量,这里的块设备是指系统上所有的磁盘和其他块设备,默认块大小是1024byte,我本机上没什么IO操作,所以一直是0,但是我曾在处理拷贝大量数据(2-3T)的机器上看过可以达到140000/s,磁盘写入速度差不多140M每秒 bo 块设备每秒发送的块数量,例如我们读取文件,bo就要大于0。bi和bo一般都要接近0,不然就是IO过于频繁,需要调整。 in 每秒CPU的中断次数,包括时间中断 cs 每秒上下文切换次数,例如我们调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目,例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。 us 用户CPU时间,我曾经在一个做加密解密很频繁的服务器上,可以看到us接近100,r运行队列达到80(机器在做压力测试,性能表现不佳)。 sy 系统CPU时间,如果太高,表示系统调用时间长,例如是IO操作频繁。 id 空闲CPU时间,一般来说,id + us + sy = 100,一般我认为id是空闲CPU使用率,us是用户CPU使用率,sy是系统CPU使用率。
wt 等待IO CPU时间。 上面的值项中的cache就是表示的操作系统缓存的文件。由于此,所以大量下载同时读的时候性能还是不错的。 解决办法 通过以上的分析我们已经知道了造成问题的罪魁祸首,所以我们的解决办法就是使用proxy_buffering off配置关掉NGINX默认的缓存。关掉这个缓存后,NGINX仍会保留一个基本的MAIN BUFFER,每次从后端读取BUFFER返回内容,然后直同步返回给用户。 影响及其它解决办法 关掉BUFFER将意味着后端的WEB服务器将实时返回结果,此时NGINX的好处仅仅是可以方便扩展,性能上肯定不及直接让后端的服务器直接面对用户,因为NGINX在其中多建立了一次请求,同时后端WEB服务器的压力也得不到释放(将一直保持连接,直到请求完成)。由于一般后端单请求返回的内容小于1M,我们通过设置一个较大的buffer完全可以将返回的内容放进内存(此时后端的WEB容器就可以释放连接,由NGINX来慢慢向慢速的用户返回结果),毕竟使用代理下载是一个太特殊的例子。有两个建议
(责任编辑:IT) |