1. nginx基本使用

  • nginx -c /path/to/nginx.conf : 启动nginx
  • nginx -s reload :重新加载配置
  • nginx -t -c /path/to/nginx.conf :测试配置文件是否正确
  • nginx -t :查看配置文件的位置
  • nginx -s stop : 快速停止nginx
  • nginx -s quit :完整有序的停止nginx(即处理完所有请求后再停止服务)
  • pkill -9 nginx :强制停止Nginx
  • kill -HUP 主进程号 : 平滑重启nginx

2. location语法

Location的匹配语法大概可以分为这四类:

  1. 精准匹配:
  • = 严格匹配,优先级最高,如果这个查询匹配,那么将停止搜索并立即处理此请求。
  • ^~ 匹配开头,如果前缀匹配上就不再进行正则表达式的检测。
location = /login/demo.html {
    .....
}
location ^~ /index/ {
    .....
}
  1. 正则匹配:
  • ~ 为区分大小写匹配
  • !~为区分大小写不匹配
  • ~* 为不区分大小写匹配
  • !~*为不区分大小写不匹配
location ~ /Abc/ {              # 区分大小写
    .....
}
location ~* .(gif|jpg|jpeg)$ {  # 忽略大小写
    .....
}
  1. 普通匹配:
  • 不带任何前缀的是普通匹配,如location /image/
  • location / 匹配所有,也是普通匹配,当所有都无法匹配时最后走这个
  1. 内部跳转:
  • @ 用于服务器内部重定向(转发)的请求
location /index/ {
  error_page 404 @index_error;
}
location @index_error {
  .....
}

实际的匹配过程:

  1. 先判断精准匹配,只要命中立即返回结果,解析过程结束;
  2. 判断普通匹配,如果有多个命中,记录下来最长的命中结果,但不结束(只看长短,不看顺序);
  3. 正则匹配(带~)从上向下按顺序匹配,只要成功匹配一个就立即返回结果,解析过程结束(关心顺序,精细的正则要往前放);
  4. 普通与正则的顺序:先进行普通匹配,不论是否匹配上,都会进行正则匹配,正则匹配上会覆盖普通匹配,正则没有匹配上则返回普通匹配中记录的最长匹配;

优先级总结:
= > ^~ > ~ | ~* > 最长前缀匹配 > /

3. rewrite语法

rewrite用于更改获取资源的URL路径,有四种重写类型:

  1. break:

跳过rewrite阶段,继续本请求(当前Location块)的其它阶段,一般用在Location块内。
在下面例子中,会内部跳转到rewrite的资源/demo.html,匹配过程结束;

location  /login {
       rewrite  ^/  /demo.html  break;
       root  home/;
}
  1. last:

停止当前请求,并使用rewrite后的新URL重新发起一个新请求,又从头开始作Location匹配;

location  /login {
       rewrite  ^/  /demo.html  last; # 会以/demo.html发起请求重新进行location匹配
}
 
location  /demo.html {   # 新请求匹配到这个location,内部跳转到/demo.html
       rewrite  ^/  /demo.html  break;
       root  home/;
}
  1. redirect:

返回302临时重定向,浏览器地址会显示跳转后的URL地址。

location  /login {
       rewrite  ^/  /demo.html  redirect;
       root  home/;
}
  1. permanent

与redirect相似,只是返回301永久重定向;

不同类型对浏览器地址栏的影响:

  • 使用break和last都是nginx内部跳转,浏览器地址栏的URL不变;
  • 使用redirect和permanent相当于从客户端重新发起请求,所以浏览器地址栏会变为rewrite后的URL;

4. upstream语法

upstream用于后端webserver的负载均衡策略,一般有三种:

  1. 轮询(默认)

当请求过来时,nginx会在两个server之间轮询访问。

upstream test{
    server 192.168.0.101:8081;
    server 192.168.0.102:8081;
}
  1. 权重

负载分配比例大概为:上面的服务访问2次,下面的服务访问1次。

upstream test1 {
    server 192.168.0.101:8081 weight=2;
    server 192.168.0.102:8081 weight=1;
}
  1. ip_hash

根据请求来源ip地址的hash值来区分访问那个server,能达到同一客户端请求始终粘在一个服务器上的效果。

upstream test2 {
    ip_hash
    server 192.168.0.101:8081;
    server 192.168.0.102:8081;
}

5. proxy_pass带/

proxy_pass结尾是否带/的效果是不一样的,规则为:

  • 当proxy_pass后面带了uri/(或者/xxx/),则会用proxy_pass指定的uri来替换location匹配的uri;
  • 如果proxy_pass后面没有带/,则会把Location匹配的路径部分加入代理uri。

以一个例子来说明,假设有下面两种配置:

# 配置1
location /test/
{
	proxy_pass http://t6:8300; 
}

# 配置2
location /test/ 
{ 
	proxy_pass http://t6:8300/; # 注意末尾的 /
}

如果访问url = http://server/test/test.jsp,分别走两种配置的结果差异:

  • 使用配置1代理后,请求路径会访问http://proxy_pass/test/test.jsp,将test/ 作为根路径,请求test/路径下的资源;
  • 使用配置2代理后,请求路径会变为 http://proxy_pass/test.jsp,直接访问server的根资源。

6. IP透传

IP透传要解决的问题:

nginx作为反向代理时,默认会将请求头Remote_Addr设为$proxy_host,所以后端主机拿到的IP都是代理服务器的,这会导致有些与IP来源地区相关的业务无法work。

与来源IP相关的字段:

  1. remote_addr:

nginx内置变量,表示客户端的IP地址,但中间如果存在代理,就是最后一个代理服务器IP,由TCP三次握手产生,无法伪造。

  1. X-Forwarded-For:

简称XFF头,值的格式为:client, proxy1, proxy2,由「英文逗号 + 空格」隔开的多个部分组成, 从左向右依次为离服务器最远的设备IP、每一级代理设备的IP,最后一级代理(上一跳)不会在XFF中体现,而是作为remote_addr来传给服务器。

  1. X-Real-IP:

自定义请求头,也用来表示与当前服务器连接的真实设备IP,可能是最原始设备也可能是中间某个正向代理,取决于每一级代理是否将原始IP通过remote_addr变量一路传下来。

  1. proxy_add_x_forwarded_for:

属于nginx内置变量,表示XFF+remote_addr, 用于在Nginx作为反向代理时设置新的XFF头。

透传IP主要作下面配置:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

注意事项:

  1. 如果存在多个反向代理服务器,后端服务拿到X-Real-IP很可能就不是预期的设备IP;
  2. 原因在于:X-Real-IP的原理是在反向代理上通过 remote_addr来传递设备IP,而从第二个反向代理开始拿到的remote_addr已经变成了上一个反向代理的IP;
  3. 最终建议还是靠X-Forwarded-For来获取设备IP。

7. nginx全局变量

- arg_[PARAMETER]    # 表示请求行中名为PARAMETER的参数值。
- args               # 这个变量等于请求行中(GET请求)的参数,如:foo=123&bar=blahblah;
- is_args            # 如果有args参数,这个变量等于"?",否则等于空值""。
- http_user_agent    # 客户端agent信息
- http_cookie        # 客户端cookie信息
- query_string       # 与args相同。
- request_body_file  # 客户端请求主体信息的临时文件名。
- request_method     # 客户端请求的动作,通常为GET或POST。
- remote_addr        # 客户端的IP地址。
- remote_port        # 客户端的端口。
- request_filename   # 当前请求的文件路径,由root或alias指令与URI请求生成。
- request_uri        # 包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。不能修改。
- scheme             # HTTP方法(如http,https)。
- body_bytes_sent    # 响应时送出的body字节数数量。即使连接中断,这个数据也是精确的。
- content_length     # 请求头中的Content-length字段。
- content_type       # 请求头中的Content-Type字段。
- cookie_COOKIE      # cookie信息中名为COOKIE变量的值
- host               # 请求头中的Host字段,一般是请求的主域名。
- http_HEADER        # 名字为HEADER的头

8. map指令

作用: 创建自定义变量,通过使用 nginx 的内置变量,去匹配某些特定规则,如果匹配成功则设置某个值给自定义变量。
格式:map $var1 $var2 {...}

  • map 的 $var1 为源变量,通常可以是 nginx 的内置变量
  • $var2 是自定义变量,$var2 的值取决于 $var1 在对应表达式的匹配情况。
  • 如果一个都匹配不到则 $var2 就是 default 对应的值。

示例:

map $args $foo {
    default 0;
    debug   1;
}

释义:

  • $args是nginx内置变量;
  • 如果 $args 匹配到 debug,那么 $foo 的值会被设为 1;
  • 如果 $args一个都匹配不到,$foo 就是default定义的值,在这里就是 0;

注意:map指令适用于 Nginx 配置文件中的作用段: http{} , map 不能写在 server{},否则会报错。

9. try_files指令

作用: 按顺序检查文件是否存在,返回第一个找到的文件或文件夹,可应用的上下文:server,location段。
语法格式:

格式1:try_files file … uri;
格式2:try_files file … =code;

查找规则:

  1. 查找路径是按照给定的root或alias为根路径来查找的;
  2. 如果是格式1,当给出的file都没有匹配到时,则重新请求最后一个参数给定的uri,就是新的location匹配;
  3. 如果是格式2,若给出的file都没有匹配到,则返回code对应的http错误码;

配置示例:

location /images/ {
    root /opt/html/;
    try_files $uri   $uri/  /images/default.gif; 
}

如果请求http://127.0.0.1/images/test.gif 会依次查找

  1. 文件/opt/html/images/test.gif
  2. 文件夹 /opt/html/images/test.gif/下的index文件(末尾/表示文件夹)
  3. 请求127.0.0.1/images/default.gif

10. nginx之mime类型

有时候,当我们在浏览器中访问一个mp4文件链接时,它会直接在浏览器中播放而不是下载,就是下面mime.types文件配置在起作用:

include       mime.types;

此文件配置了所有文件后缀所对应的MIME类型(部分示意如下),会自动填充到Http的响应头Content-Type中。

types {
    text/html                             html htm shtml;
    text/css                              css;
    text/xml                              xml;
    image/gif                             gif;
    image/jpeg                            jpeg jpg;
    application/javascript                js;
    application/atom+xml                  atom;
    application/rss+xml                   rss;
    ……
    video/3gpp                            3gpp 3gp;
    video/mp2t                            ts;
    video/mp4                             mp4;
    video/mpeg                            mpeg mpg;
    video/quicktime                       mov;
    video/webm                            webm;
    video/x-flv                           flv;
    video/x-m4v                           m4v;
    video/x-mng                           mng;
    video/x-ms-asf                        asx asf;
    video/x-ms-wmv                        wmv;
    video/x-msvideo                       avi;
}

有时对于图片、视频,浏览器会自动为用户显示或播放。正是由于Web服务器在响应头中返回了一些特殊的MIME类型,浏览器根据特殊的Content-Type类型来判断是否能直接显示或播放。

如果想让浏览器直接下载文件而不是播放,则需要为location特殊指定mime,来明确告诉浏览器按文件流走下载处理:

location /download/ {
    types        { }    # 清空之前的MIME类型映射表
    default_type application/octet-stream; # 由于上条指定将mime映射表清空,所以会走这里指定默认的mime类型,
}

11. 常见状态码

1xx: 与http服务器协商信息

  • 101:更换协议,如升级Websocket

3xx:重定向

  • 301:永久重定向
  • 302:临时重定向
  • 304:表示请求的数据客户端已有,不需要重复发送,常用于缓存资源是否有变化的检测
  • 307:所请求的资源不在本地,在另一个URL处

4xx:请求错误导致服务器无法处理

  • 400:服务器不理解请求的语法
  • 401:访问受保护资源却没有提供认证证书
  • 403:资源存在,但服务器禁止访问
  • 404:资源不存在
  • 405:HTTP方法不正确,例如:允许GET,但客户端发送了PUT
  • 407:同401类似,访问代理服务需要认证
  • 409(“Conflict”):当客户端试图执行一个”会导致一个或多个资源处于不一致状态“的操作
  • 499:上游服务器处理时间过长,反向代理主动关闭连接。可以增加如下配置让代理服务器不要主动断开客户端连接;
proxy_ignore_client_abort on;

5xx:服务器错误

  • 500:服务器内部错误
  • 502:代理与遇到上游服务器无效响应
  • 503:上游服务器资源不足,无法全部处理
  • 504:代理无法连接到上游服务器

12. nginx之失效检测

12.1 节点失效配置

nginx可以通过设置max_fails(最大尝试失败次数)和fail_timeout(失效时间)进行设置,示例如下:

upstream testserver {
     server 10.255.0.203:8080 max_fails=1 fail_timeout=10s;
     server 10.255.0.204:8080 max_fails=1 fail_timeout=10s;
}

配置释义:

  • 当fail_timeout时间内失败次数达到max_fails,则nginx会对节点状会置为失效状态;
  • 在fail_timeout时间内nginx不再转发请求给此台服务;
  • 直到超过失效时间或者所有节点都失效后,该节点重新置为有效,重新探测;

如果探测所有节点均失效,没有节点可以处理请求时,会抛出如下错误:

upstream server temporarily disabled while connecting to upstream
no live upstreams while connecting to upstream

12.2 请求失败重试

proxy_next_upstream:用于指定在什么情况下Nginx会将请求转移到其他服务器上。

  • 默认情况下,Nginx只会把connect refuse和timeout状态视为错误,不包含http状态码错误;
proxy_next_upstream error timeout;
  • 可以通过proxy_next_upstream指令将500、502、503、504等错误记录到fails中,出现这些状态码时也进行重试;
location / {
    proxy_pass http://nginxretry;
    proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}

但是当请求是Post时,即使出现上面的错误,Nginx默认也不会失败重试,原因在于nginx会判断请求方法是否冥等。

12.3 冥等方法的重试

什么冥等方法?

  • 对于http方法的相同请求,如果发送多次对服务器的预期效果与单次请求的效果相同,则认为此Http方法是冥等的。
  • 常见的HTTP请求方法中,GET用来获取数据所以是幂等的,而POST用来提交数据所以是非幂等的。

如何让Post方法也能失败重试?

  • proxy_next_upstream有一个选项non_idempotent,加上此选项后,即使是非幂等请求类型(例如POST请求),发生错误后也会重试。
location / {
    proxy_pass http://nginxretry;
    proxy_next_upstream error timeout http_500 non_idempotent;;
}

但是生产环境并不建议加non_idempotent,原因在于:

  • 无论是发生500错误还是timeout,服务器上的业务可能都已经执行过了,而重试会导致非幂等方法重复执行,从而导致业务问题。

13. 断点续传

当大文件传输中断时,客户端期望从文件已经下载的地方开始继续下载,所以如果要走断点续传,首先从客户端角度需要多传一项信息:从哪里开始下载。

Range请求头就是用于这个目的,它指明客户端想要请求资源的哪一部分,一般是一个字节范围,例如下面的断点续传请求:

get /down.zip http/1.0
User-Agent: netfox
Range: bytes=2000070-    # 告诉服务器从2000070字节开始下载,前面的无需再传
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Range的完整格式:

Range: bytes=startOffset-targetOffset/sum  # 表示从startOffset读取,一直读取到targetOffset位置,读取总数为sum
 
Range: bytes=startOffset-targetOffset # 字节总数也可以去掉

服务器收到这个请求以后,返回的信息如下:

HTTP/1.1 206 Partial Content        #
content-length=104785958
content-range=bytes 2000070-106786027/106786028
date=mon, 30 apr 2001 12:55:20 gmt
etag=w/"02ca57e173c11:95b"
content-type=application/octet-stream
server=microsoft-iis/5.0
last-modified=mon, 30 apr 2001 12:55:20 gmt

服务器返回信息释义:

  • 206状态码,表明为分段下载
  • Content-Range头表明了它返回的是资源哪一部分,
  • Content-Length表明了该部分文件响应的大小。

上面基本就是断点续传原理,类似于B站看视频或者迅雷这类的 HTTP下载工具,都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。

小结

本文从实际使用角度来描述了nginx常见的配置语法,后面还会写一个使用nginx来优化性能的介绍。