前两个月帮一个做站点的朋友排查问题,他那边十万级日活,晚高峰页面加载经常慢到超时。他第一反应是服务器带宽不够,已经升了一次配置,还是没好转。我登录上去看了眼Nginx配置,用的还是安装后的默认参数,几乎没改过。很多做站点的朋友都有这个习惯,能跑就不动,觉得Nginx优化是大流量站点才需要折腾的事,小站点没必要费这个劲。其实很多时候,访问卡顿、超时这类问题,不用升配置,调好Nginx就能解决。
连接数配置的常见误区
大部分人都知道Nginx默认的worker_processes是1,多核CPU机器要改这个参数。但很多人的调整逻辑是,有几核就开几个,核越多开越多。我之前见过一台8核的服务器,用户直接把worker_processes设成了8,性能反而不如开4的时候。原因很简单,他的站点大部分请求是动态请求,要转发给后端应用处理,应用本身已经占了不少CPU资源,Nginx开太多worker反而会增加进程上下文切换的消耗,把本来给应用的CPU抢了。
还有worker_connections,默认是1024,很多人做Nginx优化的时候,直接改成十万以上,觉得连接数越大能扛的流量越多。其实每个连接都要占用一定内存,系统也有全局的最大打开文件数限制,如果你不先调系统的ulimit参数,改了worker_connections也不生效,重启的时候还会直接报错。我刚接触Nginx优化的时候就踩过这个坑,改完参数重启失败,查了半天才发现是没改系统限制。
一般来说,worker_processes设成CPU核心数的一半到全核心都可以,如果你Nginx还要兼任反向代理、处理静态资源,后端应用占CPU不多,可以开满核。如果大部分CPU都给了后端应用,那可以少开一半。worker_connections设到一万左右就足够大部分中小站点用,流量特别大再往上涨,不用上来就拉到最大。
长连接配置容易踩的坑
现在大部分站点都会用Nginx做反向代理,把请求转发给后端的应用服务。默认配置下,Nginx和后端之间用的是短连接,每次请求都要重新建连、断开,高峰的时候Nginx这边会堆大量TIME_WAIT连接,不仅占系统资源,还会让后端的连接压力变高。
很多人都知道要给Nginx和后端之间开keepalive长连接,但是开了之后往往没调好参数,反而出问题。我之前遇到过一个案例,用户开了长连接之后,闲时一切正常,一到高峰就出一堆502错误。查了之后发现,他给keepalive的最大空闲连接数设的比后端允许的大,Nginx把空闲连接都存在自己这边,要用的时候拿出来,后端已经把这个连接关了,就出了错。
还有一个更容易忽略的问题,后端服务重启或者下线之后,Nginx如果存着大量空闲长连接,会继续把请求发到已经下线的节点,导致一段时间的5xx错误。很多做Nginx优化的文章只会说要开长连接,不会说要配置连接主动检测,其实只要加一个很短的探测超时,就能避免这个问题,这个点我也是踩过坑之后才记住。
一般来说,反向代理的长连接,keepalive_timeout设到几十秒就够,不要设到几分钟那么长,最大空闲连接数不要超过后端服务允许的最大值,再加个简单的连接失效检测,就能稳定扛住高峰,比短连接省很多资源。
静态资源处理的小细节
很多站点都把静态资源放在Nginx这边处理,这部分的优化其实是Nginx优化里见效最快的,但很多人配置的不对,白瞎了性能。
最常见的是gzip压缩,很多人只开了html的压缩,忘了给css、js、json这些静态资源开,或者把压缩等级开的太高。压缩等级越高,压缩后的文件越小,越省带宽,但消耗的CPU也越多。如果你的服务器带宽够,CPU比较紧张,压缩等级开到2到4就够,不用开到最高等级,不然CPU消耗上去,反而会拖慢整体响应。我之前帮用户把gzip等级从9改成3之后,Nginx的CPU使用率降了快20%,响应速度反而快了。
还有缓存过期配置expires,很多人要么不开,要么图省事给所有静态文件都设一年的超长缓存。这样确实省流量,也省请求,但更新代码的时候,用户浏览器会一直读旧的缓存文件,页面出问题都找不到原因。其实只要分情况配置就行,带版本号哈希的静态资源,文件名本身就变,开一年缓存没问题,不带版本号的文件,开几分钟到几小时的短缓存就行,这个改起来不麻烦,能省很多后面排查问题的时间。
还有sendfile和tcp_nopush这两个参数,默认配置里tcp_nopush一般是关闭的。开了之后,Nginx会把数据攒到一个满包再发,减少网络包的数量,传输大静态文件的时候,性能提升比较明显,这个参数开了也没什么副作用,很多人做Nginx优化的时候就是忘了开,其实改一行配置就能有效果。
容易被忽略的日志性能影响
很多人做Nginx优化的时候,从头到尾都不会考虑日志的问题,觉得日志不就是写个文件吗,能占多少资源。我之前遇到过一个案例,高峰的时候服务器磁盘IO使用率超过90%,排查下来,一半以上的IO都是Nginx写访问日志占的。因为那个站点的流量不小,每秒钟上千次请求,每次请求都实时写一次磁盘,IO自然就被打满了。
其实Nginx本身有缓冲写日志的功能,就是攒够一定量的日志数据,再一次性写到磁盘里,这样能把很多小IO合并成大IO,大大降低IO的使用率。那个站点开了缓冲之后,高峰IO使用率直接降到了30%以下,卡顿问题直接解决了,都没动别的配置。
哦,还有一个和性能无关,但属于Nginx优化里基础安全的点,很多人容易忽略,就是Nginx进程的运行权限。很多人安装完之后,为了图方便,或者不小心,直接让Nginx用root用户运行,这个其实有很大的安全隐患,如果Nginx出了漏洞,攻击者就能直接拿到服务器的最高权限。哪怕不考虑安全,很多时候静态文件的权限配置不对,Nginx用root跑能打开,换普通用户就打不开,上线之后出403错误,排查半天找不到原因,所以一开始就用普通用户跑Nginx,配置对文件目录的读取权限,也是Nginx优化里要做的基础工作,不要等到出问题再改。
当然,如果你用了日志服务,把日志远程收集,那还可以考虑把本地访问日志关掉,进一步省磁盘资源,不过大部分中小站点开个缓冲就足够了,这个改动很小,效果却很明显,很多人就是不知道。
做Nginx优化不是说要把所有能调的参数都调一遍,而是要结合自己的站点场景来调整,不同的站点,优化的重点完全不一样。
如果是日活几万以内的中小站点,其实不需要做太复杂的调整,把前面说的几个基础点做好就足够:worker_processes对应调整,调好系统文件限制,开gzip,开静态缓存,给反向代理调好长连接,开日志缓冲,这几步做完,性能就能提升一大截,足够扛住日常流量,不需要折腾别的花里胡哨的参数。
如果是日活几十万甚至百万以上的大站,那还要再加几个优化点:比如给静态文件打开文件句柄缓存,减少Nginx每次读取文件的时候查文件信息的系统调用,如果你的站点有大量小文件,这个缓存的收益会特别大,因为每个小文件都要查一次元信息,缓存之后能省很多系统调用,要是都是几个G的大文件,这个缓存的收益就小很多,所以也要看自己的文件情况调整;还有如果接入流量大,可以把Nginx拆成两层,一层做接入反向代理,一层处理静态资源,分开之后更方便调整参数,也更容易排查问题。
还有一种情况,就是静态资源都存在第三方存储服务,Nginx只做动态请求的反向代理,那Nginx优化的重点就要放在连接配置和超时配置上,静态资源相关的优化就不用花太多精力,重点调长连接、连接数、超时时间这些参数就行,不要做无用功。
我自己踩过的一个最大的坑,就是一开始做Nginx优化的时候,看到网上给的配置就直接抄,不管自己的场景,结果抄来的配置反而把性能搞差了。比如网上很多人说要开大连接数,我就直接拉到十万,结果Nginx内存占了快3G,比我后端的Java应用占的还多,其实那个时候站点的总并发才几千,一万连接都用不完,完全是浪费资源。
还有一个常见的误区,就是做了Nginx优化之后,性能还是上不去,就觉得Nginx不行,要换别的软件,其实很多时候瓶颈根本不在Nginx。我之前遇到过一个用户,说做了全套Nginx优化还是卡,我帮他排查,最后发现是数据库有慢查询,一个查询要跑好几秒,后端返回就慢,Nginx再怎么调也没用。所以优化之前一定要先定位瓶颈,先看是CPU、内存、磁盘IO还是网络的问题,瓶颈在哪里再调哪里,不要上来就调Nginx参数。
还有一点要提醒,很多人改完Nginx配置,直接就reload加载,觉得reload是无损操作,不会出问题。其实如果配置写错了,reload之后老进程不会退出,会一直占着端口,下次再启动就会报错,说端口被占用。所以每次改完配置,尤其是做Nginx优化改了很多参数之后,一定要先用带-t的参数测试一下配置有没有语法错误,确认没错再reload,这个小习惯能避免很多线上故障。
从实际使用的经验来看,Nginx优化其实没有放之四海皆准的最优配置,所有的调整都是取舍。比如你的服务器CPU够,带宽小,那就可以把gzip压缩等级开高一点,牺牲CPU换带宽;如果CPU不够,带宽够,那就把压缩等级降下来,把CPU留给更重要的请求。调整完参数之后,最好自己用压测工具跑一下,看看不同参数下的QPS、平均延迟、峰值延迟,选最适合自己当前场景的参数,不要盲目相信网上的所谓最优配置。
很多人觉得Nginx优化是高阶运维才要做的事,普通开发者不用学,其实不是,大部分普通开发者搭站点,都是用默认配置,只要花十几分钟调几个基础参数,就能把性能提上去,还能省不少服务器的资源,这个投入产出比其实非常高。