前言

Nginx 现在几乎是众多大型网站的必用技术,大家应该都知道 Nginx 被 F5 收购 的大事件,章亦春也在专心维护 OpenResty 项目构建和谐家园,无论你选择 Nginx 还是 OpenResty,都需要对 Nginx 有一个比较全面的了解,日后才能做到事半功倍。本文以开发者必备的 Nginx 基础知识为主,在参考文章中罗列了目前比较优秀的 Nginx 和 OpenResty 参考教程,希望对大家有帮助。

Nginx 基础知识从小白到入门

更新历史

2020 年 02 月 25 日 - 更新 nginx 基础知识
2019 年 11 月 12 日 - 初稿

阅读原文 - https://wsgzao.github.io/post/nginx/

扩展阅读

nginx.org

NGINX Plus

OpenResty


Nginx 基础知识

Nginx 是什么?

Nginx 是一个 web 服务器,主要处理客户端和服务器的请求分发。

特点和优势

  1. 高并发
  2. 热部署
  3. 低功耗
  4. 热部署

使用和扩展

开源免费的 Nginx 与商业版 Nginx Plus,与之对应的是免费 OpenResty 与商业版 OpenResty

陶辉《深入理解 Nginx》作者在极客时间上的讲义 PDF 已经介绍的非常详细了,如果觉得课程不错可以选择购买尽量少走弯路

极客时间:nginx 核心知识 100 讲配置文件与代码分享

nginx 正向代理与反向代理

为了便于理解,首先先来了解一下一些基础知识,nginx 是一个高性能的反向代理服务器那么什么是反向代理呢?

代理是在服务器和客户端之间假设的一层服务器,代理将接收客户端的请求并将它转发给服务器,然后将服务端的响应转发给客户端。

不管是正向代理还是反向代理,实现的都是上面的功能。

如果你对 OSI 七层模型与 TCP/IP 四层模型 不是很熟悉可以再回顾下

正向代理

正向代理(forward)意思是一个位于客户端和原始服务器 (origin server) 之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标 (原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。

正向代理是为我们服务的,即为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。

正向代理对我们是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。

反向代理

反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。

反向代理对服务端是透明的,对我们是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。

nginx 基本配置

安装 nginx 时通常需要编译自己需要的模块,可以使用 rpmbuild 制作 Nginx 的 RPM 包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
main                                # 全局配置

events { # nginx 工作模式配置
}

http { # http 设置
....

server { # 服务器主机配置
....
location { # 路由配置
....
}

location path {
....
}

location otherpath {
....
}
}

server {
....

location {
....
}
}

upstream name { # 负载均衡配置
....
}
}

如果想要生成 nginx 规范配置,可以参考nginxconfig.io

下面是 nginx 一些配置中常用的内置全局变量,你可以在配置的任何位置使用它们。

变量名 功能
$host 请求信息中的 Host,如果请求中没有 Host 行,则等于设置的服务器名
$request_method 客户端请求类型,如 GETPOST
$remote_addr 客户端的 IP 地址
$args 请求中的参数
$content_length 请求头中的 Content-length 字段
$http_user_agent 客户端 agent 信息
$http_cookie 客户端 cookie 信息
$remote_addr 客户端的 IP 地址
$remote_port 客户端的端口
$server_protocol 请求使用的协议,如 HTTP/1.0HTTP/1.1\
$server_addr 服务器地址
$server_name 服务器名称
$server_port 服务器的端口号

location 匹配规则

语法规则:location [=|~|~*|^~] /uri/ { … }

模式 含义
location = /uri \= 表示精确匹配,只有完全匹配上才能生效
location ^~ /uri ^~ 开头对 URL 路径进行前缀匹配,并且在正则之前。
location ~ pattern 开头表示区分大小写的正则匹配
location ~* pattern 开头表示不区分大小写的正则匹配
location /uri 不带任何修饰符,也表示前缀匹配,但是在正则匹配之后
location / 通用匹配,任何未匹配到其它 location 的请求都会匹配到,相当于 switch 中的 default

前缀匹配时,Nginx 不对 url 做编码,因此请求为 /static/20%/aa,可以被规则 ^~ /static/ /aa 匹配到(注意是空格)

多个 location 配置的情况下匹配顺序为:

  • 首先精确匹配 =
  • 其次前缀匹配 ^~
  • 其次是按文件中顺序的正则匹配
  • 然后匹配不带任何修饰的前缀匹配。
  • 最后是交给 / 通用匹配
  • 当有匹配成功时候,停止匹配,按当前匹配规则处理请求

意:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。比如在前缀匹配:location /dir01location /dir01/dir02,如有请求 http://localhost/dir01/dir02/file 将最终匹配到 location /dir01/dir02

例子,有如下匹配规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
location = / {
echo "规则 A";
}
location = /login {
echo "规则 B";
}
location ^~ /static/ {
echo "规则 C";
}
location ^~ /static/files {
echo "规则 X";
}
location ~ \.(gif|jpg|png|js|css)$ {
echo "规则 D";
}
location ~* \.png$ {
echo "规则 E";
}
location /img {
echo "规则 Y";
}
location / {
echo "规则 F";
}

那么产生的效果如下:

  • 访问根目录 /,比如 http://localhost/ 将匹配 规则 A
  • 访问 http://localhost/login 将匹配 规则 Bhttp://localhost/register 则匹配 规则 F
  • 访问 http://localhost/static/a.html 将匹配 规则 C
  • 访问 http://localhost/static/files/a.exe 将匹配 规则 X,虽然 规则 C 也能匹配到,但因为最大匹配原则,最终选中了 规则 X。你可以测试下,去掉规则 X ,则当前 URL 会匹配上 规则 C
  • 访问 http://localhost/a.gif, http://localhost/b.jpg 将匹配 规则 D规则 E ,但是 规则 D 顺序优先,规则 E 不起作用,而 http://localhost/static/c.png 则优先匹配到 规则 C
  • 访问 http://localhost/a.PNG 则匹配 规则 E ,而不会匹配 规则 D ,因为 规则 E 不区分大小写。
  • 访问 http://localhost/img/a.gif 会匹配上 规则 D, 虽然 规则 Y 也可以匹配上,但是因为正则匹配优先,而忽略了 规则 Y
  • 访问 http://localhost/img/a.tiff 会匹配上 规则 Y

访问 http://localhost/category/id/1111 则最终匹配到规则 F ,因为以上规则都不匹配,这个时候应该是 Nginx 转发请求给后端应用服务器,比如 FastCGI(php),tomcat(jsp),Nginx 作为反向代理服务器存在。

理解 Nginx 中 Server 和 Location 的匹配逻辑

nginx 日志

Nginx 日志主要有两种:access_log(访问日志) 和 error_log(错误日志)。

access_log 访问日志

access_log 主要记录客户端访问 Nginx 的每一个请求,格式可以自定义。通过 access_log 你可以得到用户地域来源、跳转来源、使用终端、某个 URL 访问量等相关信息。

log_format 指令用于定义日志的格式,语法: log_format name string; 其中 name 表示格式名称,string 表示定义的格式字符串。log_format 有一个默认的无需设置的组合日志格式。

默认的无需设置的组合日志格式

1
2
3
log_format combined '$remote_addr - $remote_user  [$time_local]  '
' "$request" $status $body_bytes_sent '
' "$http_referer" "$http_user_agent" ';

access_log 指令用来指定访问日志文件的存放路径(包含日志文件名)、格式和缓存大小,语法:access_log path [format_name [buffer=size | off]]; 其中 path 表示访问日志存放路径,format_name 表示访问日志格式名称,buffer 表示缓存大小,off 表示关闭访问日志。

log_format 使用示例:在 access.log 中记录客户端 IP 地址、请求状态和请求时间

1
2
log_format myformat '$remote_addr  $status  $time_local';
access_log logs/access.log myformat;

需要注意的是:log_format 配置必须放在 http 内,否则会出现警告。Nginx 进程设置的用户和组必须对日志路径有创建文件的权限,否则,会报错。

定义日志使用的字段及其作用:

字段 作用
$remote_addr 与 $http_x_forwarded_for 记录客户端 IP 地址
$remote_user 记录客户端用户名称
$request 记录请求的 URI 和 HTTP 协议
$status 记录请求状态
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小
$bytes_sent 发送给客户端的总字节数
$connection 连接的序列号
$connection_requests 当前通过一个连接获得的请求数量
$msec 日志写入时间。单位为秒,精度是毫秒
$pipe 如果请求是通过 HTTP 流水线 (pipelined) 发送,pipe 值为“p”,否则为“.”
$http_referer 记录从哪个页面链接访问过来的
$http_user_agent 记录客户端浏览器相关信息
$request_length 请求的长度(包括请求行,请求头和请求正文)
$request_time 请求处理时间,单位为秒,精度毫秒
$time_iso8601 ISO8601 标准格式下的本地时间
$time_local 记录访问时间与时区

error_log 错误日志

error_log 主要记录客户端访问 Nginx 出错时的日志,格式不支持自定义。通过查看错误日志,你可以得到系统某个服务或 server 的性能瓶颈等。因此,将日志利用好,你可以得到很多有价值的信息。

error_log 指令用来指定错误日志,语法: error_log path [level]; 其中 path 表示错误日志存放路径,level 表示错误日志等级,日志等级包括 debug、info、notice、warn、error、crit、alert、emerg,从左至右,日志详细程度逐级递减,即 debug 最详细,emerg 最少,默认为 error。

注意:error_log off 并不能关闭错误日志记录,此时日志信息会被写入到文件名为 off 的文件当中。如果要关闭错误日志记录,可以使用如下配置:

Linux 系统把存储位置设置为空设备

1
2
3
4
5
error_log /dev/null;

http {
# ...
}

Windows 系统把存储位置设置为空设备

1
2
3
4
5
error_log nul;

http {
# ...
}

另外 Linux 系统可以使用 tail 命令方便的查阅正在改变的文件, tail -f filename 会把 filename 里最尾部的内容显示在屏幕上, 并且不断刷新, 使你看到最新的文件内容。Windows 系统没有这个命令,你可以在网上找到动态查看文件的工具。

nginx 负载均衡

Upstream 指定后端服务器地址列表,在 server 中拦截响应请求,并将请求转发到 Upstream 中配置的服务器列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
upstream balanceServer {
server 10.1.22.33:12345;
server 10.1.22.34:12345;
server 10.1.22.35:12345;
}

server {
server_name fe.server.com;
listen 80;
location /api {
proxy_pass http://balanceServer;
}
}

上面的配置只是指定了 nginx 需要转发的服务端列表,并没有指定分配策略。

默认情况下采用的是轮询策略,将所有客户端请求轮询分配给服务端。这种策略是可以正常工作的,但是如果其中某一台服务器压力太大,出现延迟,会影响所有分配在这台服务器下的用户。

nginx 常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 快速关闭 Nginx,可能不保存相关信息,并迅速终止 web 服务
nginx -s stop
# 平稳关闭 Nginx,保存相关信息,有安排的结束 web 服务
nginx -s quit
# 因改变了 Nginx 相关配置,需要重新加载配置而重载
nginx -s reload
# 重新打开日志文件
nginx -s reopen
# 为 Nginx 指定一个配置文件,来代替缺省的
nginx -c filename
# 不运行,而仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件
nginx -t
# 显示 nginx 的版本
nginx -v
# 显示 nginx 的版本,编译器版本和配置参数
nginx -V
# 格式换显示 nginx 配置参数
2>&1 nginx -V | xargs -n1
2>&1 nginx -V | xargs -n1 | grep lua

参考文章

以上内容只是 nginx 的冰山一角,我个人推荐大家跟着官方文档或者类似极客时间的教程学习,可以少走很多弯路

nginx documentation

前端开发者必备的 nginx 知识

百万并发下 Nginx 的优化之道

agentzh 的 Nginx 教程

OpenResty 最佳实践

以下为极客时间专栏

Nginx 核心知识 100 讲

极客时间:nginx 核心知识 100 讲配置文件与代码分享

OpenResty 从入门到实战

文章目录
  1. 1. 前言
  2. 2. 更新历史
  3. 3. Nginx 基础知识
  4. 4. nginx 正向代理与反向代理
  5. 5. nginx 基本配置
  6. 6. location 匹配规则
  7. 7. nginx 日志
    1. 7.1. access_log 访问日志
    2. 7.2. error_log 错误日志
  8. 8. nginx 负载均衡
  9. 9. nginx 常用命令
  10. 10. 参考文章