Nginx指引
通过本文,你将了解Nginx的基本使用和Nginx的基本知识。
Nginx 安装(Ubuntu)
直接使用apt命令安装nginx:
1 | 更新源 |
Nginx的启用、停止和重新加载配置
nginx启动以后,可以通过”nginx -s <signal>“的指令来对nginx执行启用、停止、和重新加载配置文件的操作,singnal说明如下:
- stop: 快速关闭
- quit: 优雅关闭(等处理完所有处理中的请求后再关闭)
- reload: 重新加载配置文件
- reopen: 重新打开日志文件
例如:我们要等所有当前正在处理的请求结束后停止nginx进程,我们可以执行以下命令:
nginx -s quit
当我们修改了nginx的配置文件后,并不会马上生效。要使其生效需要执行以下命令:
nginx -s reload
一旦nginx的master 进程接收到reload指令,它会先去检查config文件的合法性并尝试应用新的配置。如果可以成功应用,master进程将开启一个新的worder进程并发送一个关闭命令的消息到旧的worker进程。否则master进程将回滚更改并继续使用旧配置。旧的worker进程收到关闭命令后,停止接受新的连接并继续处理正在处理中的请求,直到所有请求都处理完毕后结束进程。
配置文件结构
nginx由配置文件中指定的指令控制模块组成。指令分为简单指令和块指令。简单指令由名称和参数组成,以空格分隔并以分号(;)结尾。块指令结构同简单指令一样,不同的是块指令通过{}来包住指令,如果块指令包含了其他指令,这个被称为上下文(context)。比如:events、http、server、location。
放置在配置文件中其他任何上下文中的指令,可以称为主上下文(main context)。events和http指令在主上下文里,server在http指令中,location在server指令中。
#符号及之后的内容是注释。
部署静态页面
web服务器一项重要的任务就是提供静态文件的访问。下面,我们将实现通过不同的URL来访问服务器上不同路径下文件:/data/wwww目录下面存放静态文件,/data/images目录现面存放图片,我们通过配置location来实现通过url(http://127.0.0.1)访问/data/wwww目录下的index.html文件,通过(http://127.0.0.1/images/avta.png)访问/data/images目录下的avta.png图片。
1.在/etc/nginx/conf.d/目录下创建test.conf文件,文件内容如下:
1 | server { |
2.重新加载配置文件
sudo nginx -s reload
3.浏览器访问nginx下的文件
127.0.0.1
127.0.0.1/images/avta.png
设置简单的代理服务
nginx的常见用途之一是服务器代理。服务器接受处理请求,把请求结果返回给代理服务,代理服务返回给客户端。
现在我们在服务器上5000端口有个服务,服务器上访问如下结果:
1 | curl http://127.0.0.1:5000/v2/_catalog |
nginx对外暴露端口80,IP地址http://172.22.15.212/,现在当我们访问http://172.22.15.212/v2/_catalog能请求到5000端口的内容。
配置如下:
1 | server { |
重新加载配置nginx -s reload后,访问http://172.22.15.212/v2/_catalog后返回
1 | {"repositories":["ingress","nginx"]} |
Nginx怎么处理一个请求
基于名称的虚拟服务(Name-based virtual servers)
nginx首先决定收到的请求由哪个服务/服务器来进行处理。如下例子,首先有三个服务都监听80端口,配置如下:
1 | server { |
在上面的配置中,nginx先根据请求的header中的host字段来决定该请求由哪个服务去处理,如果都没有匹配上,则让默认的配置去处理。在上面的例子中,默认的处理则是第一个server(nginx默认的方式)。也可以通过在listen指令后面添加default_server显式的设置默认的处理服务,如下:
1 | server { |
default_server 要在0.8.21及以上版本支持,更早的版本请使用default
如何禁止处理未定义的服务名称
如果不允许处理在header中携带host字段的请求,可以如下定义:
1 | server { |
在上面,通过设置server_name为一个空的字符串,它将匹配没有“Host”头字段的请求,并返回一个特殊的 nginx 非标准代码 444 来关闭连接。
基于名称和IP的混合虚拟服务
让我们来看一个监听不同IP地址的配置:
1 | server { |
在此配置中,nginx 首先根据服务器块的监听指令测试请求的 IP 地址和端口。然后,它根据与 IP 地址和端口匹配的服务器块的 server_name 条目测试请求的“主机”标头字段。如果server_name未匹配到,则采用默认的servier处理。例如在IP为192.168.1.1:80端口收到域名为www.example.com的请求,他将采用192.168.1.1:80 默认server,即第一个配置处理。
如前所述,默认服务器是监听端口的一个属性,可以为不同的端口定义不同的默认服务器:
1 | server { |
一个简单的PHP站点配置
下面我们通过一个简单的例子来看看nginx如何来为一个请求选择对应的服务:
1 | server { |
无论上面的location顺序如何,nginx 首先搜索由文字字符串给出的最具体的前缀位置。在上面的配置中,唯一的前缀位置是“/”,并且由于它匹配任何请求,因此将用作最后的手段。然后 nginx 按照配置文件中列出的顺序检查正则表达式给出的位置。第一个匹配的表达式停止搜索,nginx 将使用这个位置。如果没有正则表达式匹配请求,则 nginx 使用之前找到的最具体的前缀位置。
需要注意的,所有类型的location仅匹配不带参数的请求部分。
现在让我们看看上面的配置如何处理请求:
- “/logo.gif”,首先匹配到“/”location,然后匹配到正则表达式
\.(gif|jpg|png)$
,因此,它会被后面的location处理,使用root /data/wwww
指令匹配到文件 /data/www/logo.gif返回给客户端。 - “/inde.php”,也是首先匹配到“/”location,然后匹配到正则表达式
\.php$
。因此,它被后面的location处理,并将请求传给监听localhost:9000
的FastCGI服务,fastcgi_param
指令将 FastCGI 参数SCRIPT_FILENAME
设置为“/data/www/index.php”
,FastCGI 服务器执行该文件。变量 $document_root 等于 root 指令的值,变量 $fastcgi_script_name 等于请求 URI,即“/index.php”。 - “/about.html”,只匹配到“/”location,因此在该位置处理。使用指令
“root /data/www”
将请求映射到文件/data/www/about.html
并返回客户端。 - “/”,这个请求相对比较复杂,首先它匹配到“/”location,然后index指令会根据root指令来判断是否存在
/data/www/index.html
文件,如果不存在,则查找index.php文件,若存在则该指令执行内部重定向到“/index.php”,并且 nginx 再次搜索location,就好像请求是由客户端发送的一样。正如我们之前看到的,重定向的请求最终将由 FastCGI 服务器处理。
Server配置中的server_name
server块指令中的server_name指令用于决定路由请求到哪一个服务器去处理。它支持精确匹配、通配符以及正则。看下面例子:
1 | server { |
通过名称路由服务,当匹配多个时,比如通配符和正则,将按照下面的规则来进行选择最终匹配:
- 精确匹配
- 以*开头的最长匹配,如:*.example.org
- 以*结尾的最长匹配,如:mail.*
- 按照在文件出现的顺序,第一个匹配的正则表达式
通配符
*通配符只能在名称的开始或者结尾,并且时在.
的边上。像www.*.example.org
和w*.example.org
都是不合法的。可以使用正则表达式指定这些名称,例如~^www\..+\.example\.org$
和~^w.*\.example\.org$
。星号可以匹配多个名称部分。名称*.example.org
不仅匹配 www.example.org
,还匹配 www.sub.example.org
。
注意:.example.org
形式的特殊通配符名称可用于匹配确切名称example.org
和通配符名称*.example.org
正则表达式
nginx 使用的正则表达式与 Perl 编程语言 (PCRE) 使用的正则表达式兼容。要使用正则表达式,服务器名称必须以波浪号字符开头:
1 | server_name ~^www\d+\.example\.net$; |
否则,它将被视为精确匹配名称,或者如果表达式包含星号,则将其视为通配符名称(并且很可能被视为无效名称)。不要忘记设置“^”和“$”锚点。它们在语法上不是必需的,但在逻辑上是必需的。另请注意,域名点应使用反斜杠进行转义。应引用包含字符“{”和“}”的正则表达式:
1 | server_name "~^(?<name>\w\d{1,3}+)\.example\.net$"; |
否则 nginx 将无法启动并显示错误消息:
1 | directive "server_name" is not terminated by ";" in ... |
命名的正则表达式捕获可以稍后用作变量:
1 | server { |
PCRE 库支持使用以下语法的命名捕获:
1 | ?<name> Perl 5.10 compatible syntax, supported since PCRE-7.0 |
如果 nginx 无法启动并显示错误消息:
1 | pcre_compile() failed: unrecognized character after (?< in ... |
这意味着 PCRE 库很旧,应该尝试使用语法“?P
1 | server { |
但是,这种用法应仅限于简单的情况(如上述),因为数字参考很容易被覆盖。
其他
有一些名称会被特殊处理。
如果需要在非默认的server块中处理没有host头的请求,可以指定一个空字符串的名称,如下:
1 | server { |
如果server_name指令未在server块中指定,则使用空字符串作为server_name。如果server_name 定义为$hostname
,则使用hostname。
如果有人使用 IP 地址而不是服务器名称发出请求,“Host”请求头字段将包含 IP 地址,并且可以使用 IP 地址作为服务器名称来处理请求:
1 | server { |
在包罗万象的服务器示例中,可以看到奇怪的名称“_”:
1 | server { |
上面没有任何特别之处,只是一个无效域名。还有其他诸如--
和!@#
之类。
国际化名称
国际化域名应该在server_name中只用ASCII表示,如
1 | server { |
优化建议
精确匹配名称、以*开头的通配符和以*结尾的通配符是和端口绑定,保存在三张哈希表里中。哈希表的大小在配置阶段进行了优化,以便可以找到具有最少 CPU 缓存未命中的名称。
匹配过程顺序是 精确名称>以*开头的通配符>以*结尾的通配符>正则,所以建议是对于server_name的配置尽量是精确更好。
如果定义了大量服务器名称,或者定义了异常长的服务器名称,则可能需要在 http 级别调整 server_names_hash_max_size 和 server_names_hash_bucket_size 指令。 server_names_hash_bucket_size 指令的默认值可能等于 32、64 或其他值,具体取决于 CPU 缓存行大小。
nginx作为负载均衡
nginx 支持以下负载均衡机制:
- 轮询:对请求以循环的方式进行分发
- 最少连接:下一个请求分配给活动连接数最少的服务器
- IP哈希:使用散列函数确定应该为下一个请求选择哪个服务器(基于客户端的 IP 地址)
默认是使用轮询方式,如下:
1 | upstream myapp1 { |
如果服务器的性能不同,也可以根据不同的配置性能来进行权重分配,这样在性能高的服务器能接受更多的请求,配置如下:
1 | upstream myapp1 { |
需要注意的是,轮询和最少连接方法两次请求可能会分发到不同的服务器,所以需要对session 做些处理,能保证即便分发不同的服务器,能正常的处理会话。
如果需要保证同个客户端的多次请求被分发到同个服务器,可以使用IP哈希的方式,配置如下:
1 | upstream myapp1 { |
作为负载均衡,nginx支持服务器健康检查。如果来自特定服务器的响应失败并出现错误,nginx 会将此服务器标记为失败,并在一段时间内尝试避免为后续入站请求选择此服务器。
max_fails
指令设置在 fail_timeout
期间应该发生的与服务器通信的连续不成功尝试次数。默认情况下,max_fails
设置为 1。设置为 0 时,将禁用此服务器的健康检查。 fail_timeout
参数还定义了服务器将被标记为失败的时间。在服务器故障后的 fail_timeout
间隔之后,nginx 将开始使用实时客户端的请求优雅地探测服务器。如果探测成功,则服务器被标记为活动服务器。