Dimitri Aivaliotis
Nginx最初的设计,是成为一个HTTP服务器,一个能解决C10K问题的HTTP服务器。关于C10K这个问题,Daniel Kegel在http://www.kegel.com/c10k.html页面有具体描述,它旨在设计一个同时连接处理10000连接数的Web服务器。为了实现这个目标,Nginx通过基于事件的连接—处理机制,并且操作系统也要使用相应的事件机制,便可以解决C10K问题。
每一个指令行都由分号结束(;),这标记着一行的结束。大括号({})实际上表示一个新配置的上下文(context)
全局配置部分被用于配置对整个server都有效的参数
在Nginx的配置文件中,include文件可以在任何地方,以便增强配置文件的可读性,并且能够使得部分配置文件重新使用。
在路径中出现通配符,表示可以配置多个文件。
Socket指令描述了Nginx如何设置创建TCP套接字的变量选项。
任何由关键字server开始的部分都被称作“虚拟服务器”部分。它描述的是一组根据不同的server_name指令逻辑分割的资源,这些虚拟服务器响应HTTP请求,因此它们都包含在http部分中。
server_name指令是相当简单的,但可以用来解决一些配置问题。它的默认值为"",这意味着server部分没有server_name指令,对于没有设置Host头字段的请求,它将会匹配该server处理。这种情况可用于如丢弃这种缺乏Host头的请求。
在这个例子中,使用的HTTP非标准代码444将会使得Nginx立即关闭一个连接。
通过在域名前面加上波浪号(~),正则表达式也可以被作为参数应用于server_name。
2.将Host头字段作为一个字符串匹配server_name指令。 3.将Host头字段与server_name指令值字符串的开始部分做匹配。 4.将Host头字段与server_name指令值字符串的结尾部分做匹配。 5.将Host头字段与server_name指令值进行正则表达式匹配。 6.如果所有Host头匹配失败,那么将会转向listen指令标记的default_server。
7.如果所有的Host头匹配失败,并且没有default_server,那么将会转向第一个server的listen指令,以满足第1步。
◆ 没有正则表达式的location被作为最佳的匹配,独立于含有正则表达式的location顺序。
这里比较匹配描述的是解码URI,例如,在URI中的"%!"(MISSING),将会匹配location中的" "(空格)。
try_files指令将会按照给定它的参数列出顺序进行尝试,第一个被匹配的将会被使用。它经常被用于从一个变量去匹配一个可能的文件,然后将处理传递到一个命名location,如下面的示例所示。
除以下前缀外,locations可以被嵌套。 ◆ 具有"=" 前缀。 ◆ 命名location。 最佳实践表明正则表达式location被嵌套在基于字符串的location内,如下面的示例所示。  # first, we enter through the root location / { # then we find a most-specific substring # note that this is not a regular expression location ^~ /css {  # here is the regular expression that then gets matched location ~* /css/.*.css$ {  }  }  }
Nginx设计为不但能够提供Web服务,而且还提供了邮件代理服务。在本章中,你将会学习到如何将Nginx配置为一个代理POP3、IMAP和SMTP的服务器。
在错误日志中,每一行对应于一个特定的日志级别,通过配置使用error_log指令实现。不同的日志级别有debug、info、notice、warn、error、crit、alert和emerg,这个顺序是按重要程度递增。配置一个特定的级别将包含本级别所有以上更严重级别的信息,默认的日志级别是error。
或者在错误日志中你看到worker_connections exceed open file resource limit消息,你就会知道你需要增加这个值。
如果你观察到是由于现有的TCP端口耗竭而造成的连接限制,那么你需要增加临时端口范围。这是TCP端口范围,你的操作系统用于出栈连接,它的默认值为5000,但是通常设置为16384。
反向代理(reverse proxy)是一个Web服务器,它终结了客户端连接,并且生成了另一个新连接,新连接代表客户端向上游服务器(upstream server)生成连接。上游服务器被定义为一个Nginx产生连接“打通”了客户端请求的服务器。这些上游服务器采取各种形式,而Nginx可以采取不同的配置来处理它们。
有时,一个上游服务器可能无法满足要求。Nginx有能力直接从上游服务器,从本地磁盘,或作为一个完全不同服务器的网页重定向给客户投递一个错误的信息。
由于反向代理的性质,上游服务器不会直接从客户端获取信息。这些信息的某些部分,例如,客户端真实的IP地址,也包括跟踪请求,对于调试很重要。这些信息可能通过头的形式被传递到上游服务器。
Nginx能够作为一个反向代理来终结来自于客户端的请求,并且向上游服务器打开一个新请求。在这个处理的过程中,为了更好地响应客户端请求,该请求可以根据它的URI、客户机参数或者一些其他的逻辑进行拆分。通过代理服务器,请求的原始URL中的任何部分都能够以这种方式进行转换。
代理到上游服务器的配置中,最重要的是proxy_pass指令。该指令有一个参数,URL请求将会被转换,带有URI部分的proxy_pass指令将会使用该URI替代request_uri部分。例如,下面例子中的/uri,在请求传递到上游服务器时将会被替换为/newuri。
然而,这个规则有两个例外的情况。首先,如果location定义了一个正则表达式,那么在URI部分没有转换发生。在这个例子中,URI /local将被直接传递到上游服务器,而不会如期地转换为/foreign。
◆ X-Real-IP头和X-Forwarded-For头有相似的目的,都用于转发连接客户端IP地址到上游服务器得到信息。
◆ proxy_buffer_size、proxy_buffers和proxy_busy_buffers_size指令将会在下一章详细讨论。总而言之,这些缓冲控制了Nginx如何快速地响应用户的请求。
下面的配置将重写cookie的域和路径,以便匹配新的应用端点。
keepalive指令特别值得一提,Nginx服务器将会为每一个worker进程保持同上游服务器的连接。在Nginx需要同上游服务器持续保持一定数量的打开连接时,连接缓存非常有用。如果上游服务器通过HTTP进行“对话”,那么Nginx将会使用HTTP/1.1协议的持久连接机制维护这些打开的连接。
我们明确指定了Nginx要同运行在本机8080端口的Apache保持32个打开的连接。起初,Nginx仅需要为每一个worker打开32个TCP握手连接,然后通过不发送close的Connection头保持这些连接的打开。使用了proxy_http_version,并且指定了我们愿意使用HTTP/1.1同上游服务器进行通信。
如果从默认的轮询(round-robin)负载均衡算法切换为ip_hash或者least_conn,那么我们需要在使用keepalive指令之前指定负载均衡算法。
典型的Apache配置由于资源限制无法同时处理更多的请求。然而,Nginx被设计为处理这种类型的流量,使用很少的资源并且表现得很好。大多数CMS预配置为Apache,集成使用了.htaccess文件扩展配置,因此使用Nginx的长处就是通过代理来连接Apache实例。
可能这是一个最基本的代理配置,Nginx将会终止所有的客户端连接,然后将代理所有请求到本地主机的TCP协议的8080端口上。此处我们假设Apache已配置为在localhost:8080上监听。
指令try_files(包括在http core模块内)意味着按顺序尝试,直到找到一个匹配为止。因此,在上面的例子中,Nginx将会投递在根路径(/)中查找到的任何文件,这些文件与客户端给定的URI相匹配。如果没有找到任何匹配的文件,那么将会把请求代理到Apache并做进一步处理。在这里,我们使用了一个命名的location,在本地请求尝试不成功时转至代理处理。
可能需要配置Nginx将请求传递到多个上游服务器,这可以通过upstream来声明,定义多个server可以参考upstream中的proxy_pass指令。 
如果一个客户端总是希望到达同一个上游服务器来改善传统的会话粘滞性(session- stickiness),则应该使用ip_hash指令。当发出的请求导致每一个请求的响应时间长短不一,那么应该选择使用least_conn算法。在一般情况下,默认的轮询算法使用无论是客户端,还是上游服务器端均勿需特别考量。
round-robin)、IP哈希(IP hash)和最少连接数(Least Connection),你可以使用其中的一种来选择哪一个上游服务器将会在下一步中被连接。在默认情况下,使用轮询算法,它不需要配置指令来激活。该算法选择下一个服务器,基于先前选择,在配置文件中哪一个是下一个服务器,以及每一个服务器的负载权重。轮询算法是基于在队列中谁是下一个的原理确保将访问量均匀地分配给每一个上游服务器的。
IP哈希(IP hash)算法通过ip_hash指令激活使用,从而将某些IP地址映射到同一个上游服务器。Nginx通过IPv4地址的前3个字节或者整个IPv6地址作为哈希键来实现。同一个IP地址池地址总是被映射到同一个上游服务器,所以,这个机制的目的不是要确保公平分配给每一台上游服务器,而是在客户端和上游服务器之间实现一致映射
最少连接数可以用least_conn指令启用。该算法的目的是通过选择一个活跃的最少连接数服务器,然后将负载均匀分配给上游服务器。如果上游服务器的处理器能力不相同,那么可以为server指令使用weight参数来指示说明。该算法将考虑到不同服务器的加权最小连接数。
因此,我们看到更好的选择方式是基于URI配置多个location,正如下面示例中的配置所示。
另外还有一些上游服务器无法响应请求的情况,在这些情况下,可以将Nginx配置为从它的本地磁盘提供一个文件。
在代理到一组上游服务器的时候,你可能想定义一组外部上游服务器作为一个后备(fallback)服务器,以便在其他服务器不能提供请求时再提供请求。当使用这种后备服务器针对请求的URI来投递一个定制的响应时,这种做法非常有用。
在使用代理服务器时,客户端不能直接连接到上游服务器。因此,上游服务器不能从客户端直接获取信息。任何信息,例如客户端的IP地址,都需要通过头来传递。Nginx使用proxy_set_header指令来提供。
通过代理分开了客户端到应用程序服务器的连接,实现了安全措施。这是在一个架构中使用反向代理的主要原因之一。客户端仅直接连接运行反向代理的机器,这台机器应该足够安全,以至于攻击者找不到任何入口。
◆ 确保Nginx使用一个非特权用户运行(典型的用户www、webservd或者www-data,这依赖于具体的操作系统)。
我们首先使用listen指令的ssl参数激活了SSL模块。然后,我们指定了希望客户选择使用的服务器密码列表,这是因为我们配置服务器使用的密钥被证明是最安全的。这可以防止客户端使用过期的密码。ssl_session_cache指令被设置为shared,以便所有的worker进程能够从每一个客户端“昂贵”的SSL自动协商中获益。如果配置了同样的名字或者将该指令放置在http部分,那么多个虚拟主机可以使用同一个ssl_session_cache指令。第二和第三部分分别为缓存的名称和大小,然后,为该主机指定证书和key。注意,这个key文件的权限应该设置为仅能够被master进程读取。我们将X-FORWARDED-PROTO头的值设置为https,是为了运行在上游服务器上的应用程序能够认识到原始请求使用了HTTPS的事实。
在http部分中,通过geoip_country指令指定预编译数据库文件的位置。通过国家代码,这将会提供最有效的方式阻止、允许IP地址访问。
这仅仅是一个基于客户端IP地址解决阻止访问的网站问题的办法。其他的解决方案包括保存客户端的IP地址为一个键-值存储,为每个请求更新计数器,如果已经出现了一定时间内的过多请求,那么就阻止访问。
向上扩展(Scale Up)是指添加更多的资源到一台机器,不断增加可用资源池以满足客户的需求。向外扩展(Scale Out)意味着向有效的响应池中增加更多的机器,以免有机器因处理客户端请求而忙得不可开交。无论这些机器是云中的虚拟实例,还是在数据中心运行的物理机器,从经济效益来看应该选择向外(out),而不是向上(up)。在这里Nginx作为一个反向代理处理连接请求。
上游服务器是在给定设置的超时时间内给出响应,尽管如此,仍然需要小心当上游服务器在给定的时间内没有响应时,Nginx可能会投递504网关超时错误(504 Gateway Timeout Error)。
作为一个应用程序的反向代理,Nginx可以在许多方面进行调优。通过缓冲、缓存和压缩,通过Nginx的配置可以使客户体验尽可能地好。
优化带宽可以帮助减少响应的传输时间。Nginx有能力将一个来自上游服务器的响应在传递到客户端之前对其进行压缩。gzip模块默认启用,它经常被用于压缩反向代理的内容,这样做非常有意义。
Nginx包含一个单一的master进程和多个worker进程。所有这些进程都是单线程,并且设计为同时处理成千上万个连接。worker进程是处理连接的地方,因为这个组件就是用于处理客户端请求的。Nginx使用了操作系统事件机制来快速响应这些请求。
Nginx的worker进程运行在一个忙碌的事件循环处理中,用于处理进入的连接。每一个Nginx模块被构筑在worker中,因此任何请求处理、过滤、处理代理的连接和更多的操作都在worker进程中完成。由于这种worker模型,操作系统能够单独处理每一个进程,并且调度处理程序最佳地运行在每一个处理器内核上。如果有任何阻塞worker进程的进程,例如,磁盘I/O,那么需要配置的worker进程要多于CPU内核数,以便于处理负载。
在这里,我们限制每一个唯一IP地址访问限制在10个连接。对于通常的浏览,这个数值应该足够了,现代的浏览器每一个主机会打开两个或者三个连接。需要注意的是,在代理上网的后面可能会有很多个用户,他们都是从同一IP地址来的,所以在日志中会记录有503(Service Unavailable)错误代码,这意味着该限制已生效。 
一种可能使用的情形是,使用密码生成的MD5哈希值作为下载页面的一个链接,然后将密码配置在Nginx的配置文件中,以便能够使得用户访问这些链接。这个词和页面要定期更换,以防止以后再次调用被保存的链接,下面的例子说明了这种情况。
如果在Nginx的配置文件中添加secure_link_secret指令,并且是使用同样的密码,那么这些链接是有效的。
◆ 连接数(1);
紧急消息也意味着Nginx不响应任何请求,这也意味着Nginx没有启动,或者是如果它已经运行,这时要求读取配置,那么它将不会执行请求的变更。
对于Nginx而言,有两个主要地方能初步查找性能问题:文件描述符限制和网络限制。 9
既然我们知道Nginx将会是该机器上使用文件描述符的主要用户,那么我们可以将这个数设置得高一些。具体如何设置这个数量要依赖于特定的操作系统,你可以按照如下方法进行。 ◆ Linux:  vi /etc/security/limits.conf  www-run hard nofile 65535 $ ulimit -n 65535
在Nginx作为邮件或者HTTP代理时,它需要同上游服务器打开多个连接。为了使尽可能多地打开连接,你可以将TCP端口范围调整到最大。 ◆ Linux:  vi /etc/sysctl.conf  net.ipv4.ip_local_port_range = 1024 65535 # sysctl -p /etc/sysctl.conf
有关网络性能的一个最重要的值是监听新TCP连接队列的大小,应该增加这个值,以便接受更多的客户访问。具体如何做到这一点,选择什么样的值,这取决于使用的操作系统和优化目标。 ◆ Linux:  vi /etc/sysctl.conf  net.core.somaxconn = 3240000 # sysctl -p /etc/sysctl.conf
从localhost(例如,使用curl http://localhost/nginx_status)查看这个URI,显示的输入内容基本类似如下。  Active connections: 2532 server accepts handled requests 1476737983 1476737983 3553635810 Reading: 93 Writing: 13 Waiting: 2426 在这里我们看到有2532个打开的连接,在这些连接中有93个连接正在读取Nginx的请求头,有13个连接正在读取Nginx的请求体,处理请求或者向客户端写响应;剩余的2426个请求被看作是keepalive连接。
我们通过stub_status模块能够提供的各种信息来帮助我们排除故障。这些数据对于我们获取如何执行Nginx总体思路非常有用。
任何rewrite规则的第一部分都是一个正则表达式。像这样,可以使用圆括号定义的部分作为“捕获”,它随后就会被位置变量引用。位置变量的值取决于在正则表达式中捕获的顺序,它们被标记了数字,因此位置变量$1将会引用第一组括号,$2引用第二组,以此类推。例如,参考下面的正则表达式。