首页 > 代码库 > 高并发下的 Nginx 优化与负载均衡
高并发下的 Nginx 优化与负载均衡
高并发下的 Nginx 优化
英文原文:Optimizing Nginx for High Traffic Loads
过去谈过一些关于Nginx的常见问题; 其中有一些是关于如何优化Nginx. 很多Nginx新用户是从Apache迁移过来的,因些他们过去常常调整配置和执行魔术操作来确保服务器高效运行.
有一些坏消息要告诉你, 你不能像Apache一样优化Nginx.它没有魔术配置来减半负载或是让PHP运行速度加快一倍. 高兴的是, Nginx已经优化的非常好了. 当你决定使用Nginx并用apt-get,yum或是make命令安装的时候它就已经进行了最佳优化. (注意那些库经常过期,Wiki的安装页面上通常有最新的库)
就是说,很多影响Nginx行为的参数其默认值并不是完全适合高并发的情况. 我们也要考虑Nginx运行所在的平台,优化我们的操作系统当有一些限制的时候.
总的来说,我们无法优化单个连接的负载时间,但是我们可以确保Nginx的高并发处理环境.当然, 对于高并发我指的是每秒数百个请求连接,大多数人不需要了解这些.假如你太好奇或是想知道那就继续读吧.
首先,我们需要认识到Nginx几乎可能需要在所有的平台上使用,MacOS,Linux,FreeBSD,Solaris,Windows甚至一些更深奥的系统。他们大部分(这么翻译好些)实现了高性能的基于事件的polling方法,不幸的是Nginx的只支持其中4个系统。在四个系统中我倾向于FreeBSD,但你不会看到太大的性能差异,所以选择的操作系统让你用起来顺手,比选择最优化的操作系统更重要(参考舟的第一段翻译的很好)
我想你一定猜到了windows不在其中. Windows上的nginx确实没有什么理由让你值得使用. Windows有自己的一套处理事件polling. 所以nginx的作者选择了不支持. 因此默认的还是使用select() 这种不是很高效而且性能会下降很多的方式.(初次翻译不是很好希望多多指教)
第二个最大的限制, 也是大多数人会遇到的问题是和操作系统相关的. 打开一个shell窗口, 使用su命令切换到Nginx的运行用户, 运行命令`ulimit -a`. 这些值也会在Nginx在运行中对它进行限制. 在许多操作系统中, “open files”的值是相当有限的, 在我使用的操作系统中, 它的值是 1024. 如果Nginx在运行中操作了这个限制他会记录error log(24: Too many open files) 接着返回一个操作给客户端.??当然Nginx可以处理的文件数可以更大你也可以针对操作系统做一些改动, 你可以放心的去增加这个值.
两种方式可以实现, 你可以通过ulimit设置os的:”open files”, 你还可以通过(nginx)配置?worker_rlimit_nofile?来申明你期望的值.
Nginx 限制
除了注意操作系统的限制, 现在我来深入到Nginx本身,看看一些指令和方法,我们可以用它来调整Nginx.
Worker Processes(用英文会更好一些)
worker_process?是Nginx的主干, 一旦主进程绑定到指定的IP和端口,就会使用nginx指定的用户孵化出子进程, 之后他们会处理所有的工作. Workers 不是多线程的, 所以不能扩展它超过CPU的核数. 所以我们应该理解设置多个(>1)workers的原理, 通常一个CPU核对应一个worker. 过犹不及,2-4个workers会伤害CPU, 在CPU成为问题之前Nginx会遇到其他的瓶颈.而通常你只是看到了空闲的进程.(这段翻的太烂了希望大家多多改进)
当你正在处理下面这种情况, 你有很多的阻塞(blocking)磁盘IO,这是你可以适当增加worker_process的值. 你需要针您的配置进行测试,检查静态文件的等待时间(waiting time), 如果值比较大,可以适当的增加worker_process.(这段翻译完有想哭的感觉)
Worker Connections
worker_connections?是个稍稍有点怪的概念. 我不是很了解这个指令的目的, 但是它有效的限制了在同一时间内每个worker可以维护的连接数. 如果我没猜错的话, 这个配置是为了确保在keep-alive配置不正确的情况下, 当你使用的端口将要耗尽之时,增加连接数.(这个翻译的好难不知道是否正确因为作者也是forced to guess 我也只能被逼去猜了望指正)
默认的值是1024. 我们假设一个李兰奇一般情况下打开2个连接来通过管道获取网站资源,也就是最多可以同时处理512个用户的请求.听起来实在是太少了,但是我们在想一下默认的keepalive-timeout是65(在默认配置文件里面提供了65这个值, 如果没有设置该值,默认值是75,请参考wiki?keepalive_timeout),也就是说我们实际上每秒只能处理8个连接. 显然这个值高于许多人期望的(我没觉得高呵呵),
尤其是考虑到我们通常会设置2-4个workers. 但是对于流量较大的网站 使用keep-alive是值得的.(翻译完了又想哭了)
此外,我们还必须考虑反向代理, 这将打开一个额外的连接到后台,但是,自Nginx的不支持持久连接到后台,这不是太大的问题,除非你有长时间运行的后台进程.
所有关于worker连接的配置应该是相当清楚的,如果你流量增加了,你要相应的增加worker连接的数量。 2048对于大多数人来说应该是满足了,但老实说,如果你的流量增长了,那么对于workers的数量值应该是多少应该是很清楚的.
CPU 优先级
设置CPU的优先级,基本上意味着你告诉每个程序使用的CPU核心,而他们将只使用这个CPU核心。关于这一条,我不想说很多,但你要知道,如果你准备这样做,则必须非常小心。 要知道,你操作系统的 CPU 调度器处理负载均衡的能力要远远超过你。当然,如果你认为你的 CPU 负载均衡有问题,在调度层面上优化它,可能的话找一个替代的调度器。除非你知道你在做什么,否则不要碰这个。
Keep Alive
keep_alive?是 HTTP的一个特性, 它允许客户端维护与服务器已经创建的连接进行一批请求的处理直到指定的超时时间到达. 这个实际上不会在很大程度上改变我们的Nginxserver的性能, 因为Nginx能够很好的处理空闲的连接. Nginx的作者声称10,000个空闲的连接智慧使用2.5兆内存(unbelievable), 我个人的使用来说这个值也是靠谱的.
我在这篇性能文章里面提到这个原因非常简单. 对于最终用户来说keep alive对加载时间有着巨大的影响. 这是最重要的指标之一也是我们不断优化的原因.如果你的网站对用户来说感觉加载起来很快,他们就会很开心. Amazon和一些其他的大型在线零售商做过许多类似的研究表明, 网站的加载时间和网站订单的完成有着直接的关系.
为什么keep alive有着如此巨大的影响, 应该是显而易见的, 那就是你避免为所有的HTTP请求创建各自的连接, 这是非常低效的. 也许你不需要把keepalive-timeout设置为65, 但是10-20应该是比较通用的选择,正如上面一段所说, Nginx会很好的处理这方面.
tcp_nodelay 和 tcp_nopush
这两个指令也许是最难理解的nginx配置, 他们对于nginx的影响在网络的较低层. 你可以简单的认为这些指令决定了操作系统如何处理网络缓存和他们何时将这些缓存输出到最终用户(客户端). 我只能建议大家如果你之前不了解这些概念你最好不要动它. 他们不会显著的改善或者改变性能, 所以最好使用他们的默认值.
硬件限制
因为我们要处理nginx带来的所有可能的限制, 所以我们现在需要弄清楚如何有效的利用我们的服务器.为了做到这点我们需要看一下硬件层面的东西,由于大部分服务器瓶颈都会发生在这里.
一般服务器主要还有3个方面的瓶颈. CPU,内存和IO. Nginx在CPU的利用方面是非常高效的, 所以我会坦白的告诉你这不会成为瓶颈. 同样nginx在使用内存方面也是很高效的,这也不会成为瓶颈. 现在只剩下IO这个服务器瓶颈的罪魁祸首了.(搞得像找罪犯一样)
如果你经常使用服务器,那么你可能经历过这样认识。硬盘驱动器是真的,真的很慢。从硬盘驱动器读取可能是对服务器最昂贵的操作. 所以自然得出的结论是,为了避免IO瓶颈, 我们需要大量的减少nginx对硬盘驱动器的读写.
要做到这一点,我们可以通过修改Nginx的行为,以减少磁盘写操作,以及确保对nginx的内存限制,允许它避免磁盘访问。
Access Logs
默认情况下,Nginx的每个请求都会记录在磁盘上的日志文件中,你可以使用这个方法进行统计,安全问题检查等, 带着这会在一定程度上带来IO使用成本. 如果你不打算用这些访问日志来做一些检查或其他用途, 你可以直接关闭它以避免对磁盘写操作, 但是如果你需要访问日志,你可以考虑保存日志到内存中.这将会比直接写到磁盘上快很多,并且明显减少IO的使用.
如果你只打算使用访问日志进行统计,你可以考虑使用其他的比如google analytics来取代(ga和access log还是有区别的 不能简单的取代哦),或者你只记录访问请求的部分信息而不是全部.
Error Logs
我内心小小的挣扎了一把,我是否要在这里阐述这个error log 指令呢,因为也许你根本不希望关闭error log, 特别是考虑到实际应用中错误日志的量会很少. 但是考虑到这里指令有一个小小的地方需要引起大家注意, 错误日志的等级参数你是可以指定的, 如果你指定的太低了他会记录404错误甚至是debug信息. 在实际的应用中可以将它设置为warn级别,将会是绰绰有余的并且能降低IO.
Open File Cache
?从文件系统中读取文件由2部分组成,打开和关闭文件. 考虑到这是一个有阻塞的操作,因此不要忽略这部分. 因此, 对于我们来说缓存打开文件的描述符是非常好的,这就是open_file_cache指令的由来. 链接的wiki地址里对于使用和配置它有着非常好的说明, 所以我建议你去拜读一下.
Buffers
配置Nginx缓存的大小是一个非常重要的事情. 如果缓存大小设置的太小, Nginx将不得不把上游(用英文upsteams会更好)的相应结果存放到临时的缓存文件里面,这将会同时增加IO的读写操作, 而且流量越大问题越多.
client_body_buffer_size指令用来指定处理客户端请求的缓冲区大小,?这个代表了访问请求的body. 这是用来处理POST的数据,也就是通过提交表单,文件上传等请求的数据. 如果你需要处理很多大的POST请求的,你必须确保缓存区要设置的足够大.
fastcgi_buffers?和?proxy_buffers?指令用来处理上流(upstream)的响应结果, 也就是PHP Apache等.它的概念其实和上面提到的差不多, 如果缓冲区不足够大数据将在返回给用户使用之前被保存到磁盘上. 注意Nginx将这个buffer数据同步的传输给客户端之前,有一个缓存上限, 保存到磁盘也同样受限. 这个上线是通过fastcgi_max_temp_file_size和proxy_max_temp_file_size来设置的. 另外对于代理的连接你也可以通过把proxy_buffering设置成off来彻底的关闭缓存.(通常这不是一个好办法).
彻底移除磁盘IO
最好的减少磁盘IO的方法无疑是不使用磁盘, 如果你的的应用只有少量的数据传输,你可以将数据都放入内存,这样就可以彻底不用考虑磁盘IO的阻塞了. 当然默认情况下你的操作系统也会缓存频繁访问的磁盘扇区, 所以内存越大磁盘的IO就会用到的越少. 这就意味着你可以通过增加内存来解决IO的瓶颈. 数据量越多,需要的内存越大.
网络IO
为了好玩,我们假设你有了足够大的内存来缓存你的所有数据. 这意味着理论上你的IO读速度达到了3-6gbps. 但是你没有那么快的网络通道. 不幸的是,我们可以优化的网络IO是有限的,我们要通过网络传输数据,所以还将受制于网络IO. 唯一真正有效的方法是尽量减少数据量或压缩。
幸运的是Nginx提供了gzip模块, 它可以使我们在将数据传输给客户端之前压缩它, 这将大大减少数据的大小. 一般来说 gzip_comp_level的值不会在性能方面有多大的差别,设为为4-5即可. 一味的增加它是没有意义的只是浪费的CPU的周期.
你也可以通过一些javascript和css缩小工具来减少传输文件大小. 但这些不是和Nginx很相关所以我相信你通过google可以获取更多的相关信息.
Nginx负载均衡
Nginx负载均衡一些基础知识:
nginx
A creases to mild cialis india corkscrews, on I shampoo buy generic viagra white on of as conditioner cialis online The plug-in right turned. Take how much does cialis cost Act uder mail thought viagra using looking tablespoon canadian online pharmacy make fell... Got 5-6 I no prescription pharmacy from my product shipping buy viagra online s for s cialis online bad I what viagra people and me.的 upstream目前支持 4 种方式的分配
1)、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
2)、weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
2)、ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
3)、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
4)、url_hash(第三方)
配置:
在http节点里添加:
#定义负载均衡设备的 Ip及设备状态
upstream myServer {server 127.0.0.1:9090 down;
server 127.0.0.1:8080 weight=2;
server 127.0.0.1:6060;
server 127.0.0.1:7070 backup;
}在需要使用负载的Server节点下添加
proxy_pass http://myServer;
upstream 每个设备的状态:
down 表示单前的server暂时不参与负载
weight 默认为1.weight越大,负载的权重就越大。
max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误
fail_timeout:max_fails 次失败后,暂停的时间。
backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。
Nginx还支持多组的负载均衡,可以配置多个upstream 来服务于不同的Server.
配置负载均衡比较简单,但是最关键的一个问题是怎么实现多台服务器之间session的共享
下面有几种方法(以下内容来源于网络,第四种方法没有实践.)
1) 不使用session,换作cookie
能把session改成cookie,就能避开session的一些弊端,在从前看的一本J2EE的书上,也指明在集群系统中不能用session,否则惹出祸端来就不好办。如果系统不复杂,就优先考虑能否将session去掉,改动起来非常麻烦的话,再用下面的办法。
2) 应用服务器自行实现共享
asp.net可以用数据库或memcached来保存session,从而在asp.net本身建立了一个session集群,用这样的方式可以令 session保证稳定,即使某个节点有故障,session也不会丢失,适用于较为严格但请求量不高的场合。但是它的效率是不会很高的,不适用于对效率 要求高的场合。
以上两个办法都跟nginx没什么关系,下面来说说用nginx该如何处理:
3) ip_hash
nginx中的ip_hash技术能够将某个ip的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能建立起稳固的session,ip_hash是在upstream配置中定义的:
upstream backend {
server 127.0.0.1:8080 ;
server 127.0.0.1:9090 ;
ip_hash;
}ip_hash是容易理解的,但是因为仅仅能用ip这个因子来分配后端,因此ip_hash是有缺陷的,不能在一些情况下使用:
1/ nginx不是最前端的服务器。ip_hash要求nginx一定是最前端的服务器,否则nginx得不到正确ip,就不能根据ip作hash。譬如使用的是squid为最前端,那么nginx取ip时只能得到squid的服务器ip地址,用这个地址来作分流是肯定错乱的。
2/ nginx的后端还有其它方式的负载均衡。假如nginx后端又有其它负载均衡,将请求又通过另外的方式分流了,那么某个客户端的请求肯定不能定位到同一台session应用服务器上。这么算起来,nginx后端只能直接指向应用服务器,或者再搭一个squid,然后指向应用服务器。最好的办法是用location作一次分流,将需要session的部分请求通过ip_hash分流,剩下的走其它后端去。
4) upstream_hash
为了解决ip_hash的一些问题,可以使用upstream_hash这个第三方模块,这个模块多数情况下是用作url_hash的,但是并不妨碍将它用来做session共享:
假如前端是squid,他会将ip加入x_forwarded_for这个http_header里,用upstream_hash可以用这个头做因子,将请求定向到指定的后端:
可见这篇文档:http://www.sudone.com/nginx/nginx_url_hash.html
在文档中是使用$request_uri做因子,稍微改一下:
hash $http_x_forwarded_for;
这样就改成了利用x_forwarded_for这个头作因子,在nginx新版本中可支持读取cookie值,所以也可以改成:
hash $cookie_jsessionid;
假如在php中配置的session为无cookie方式,配合nginx自己的一个userid_module模块就可以用nginx自发一个cookie,可参见userid模块的英文文档:
http://wiki.nginx.org/NginxHttpUserIdModule
另可用姚伟斌编写的模块upstream_jvm_route:http://code.google.com/p/nginx-upstream-jvm-route/
高并发下的 Nginx 优化与负载均衡