序言
作为一名后端工程师,Nginx 的学习必不可少。
运行原理
Nginx 的进程使用经典的「Master-Worker」多进程模型模型,启动后将存在两类进程:
- Master 进程:主要用来管理 Worker 进程,包括:接收来自外界的信号,向各 Worker 进程发送信号,监控 Worker 进程的运行状态,当 Worker 进程退出后,会自动重新启动新的worker进程。
- Worker 进程:主要处理基本的网络事件,多个 Worker 进程之间是对等互相独立的,即同等竞争来自客户端的请求。一个请求,只可能在一个 Worker 进程中处理,一个 Worker 进程,不可能处理其它 Worker 进程的请求
疑问——Master 进程和 Worker 进程的个数可以分别存在几个?
对于 Nginx 而言,其只会运行一个 Master 进程,但 Worker 进程的运行个数却是可以设置的,一般会设置与机器 CPU 核数保持一致。
虚拟主机
通过 URL(协议 + 域名)去访问一个网站,会通过 DNS 服务器将域名解析成公网 IP 地址,最终才访问到 IP 地址对应的服务主机上的应用服务。
那么,思考一个问题:域名、公网 IP 、主机、主机上的应用服务这几个是什么关系?
其实,这是多个问题的集合体,我们只需要拆解问题,分别进行分析即可。
第一个问题:公网 IP 和主机之间是什么关系?
公网 IP 和主机是一对一映射关系。
在互联网上,可以将一个公网 IP 分配给某个主机。注意哦,公网 IP 对应的主机是可以配置变化的,这个决定权在分配商。举个例子,你买了一个服务器放家里,之后可以向运营商申请一个固定的公网 IP 去绑定你的服务器,这个 IP 到期后运营商可以收回,然后重新分配给其他服务器。
好了,明白了第一个结论,我们下面思考后续问题。
域名和公网 IP 是什么关系?
域名和公网 IP 是多对多映射关系。
- 一个域名可以解析到被一个 IP 地址,DNS 解析时添加 IP 对应的 A 记录即可
- 一个域名也可以被解析到多个 IP 地址,DNS 解析时多添加几个不同 IP 的 A 记录
- 多个域名可以被解析到一个相同 IP 地址,由 Web 服务器的虚拟主机实现
实际运用
思考这些问题由啥用啊,其实重点在于我们想知道:在实际场景中,经常会把多个域名配置到一个固定的 IP 上,然后由此 IP 对应的服务器上的不同服务去处理请求。
那么,为什么明明不同域名经 DNS 解析后,访问的是同一个 IP 对应的服务器,而服务器却能知道为不同域名去导向固定的不同应用服务,而不出错呢?
Linux 服务器本身无法做到该功能,这需要 Web 服务器(如 Apache、Nginx)的虚拟主机提供此功能。
举个例子:假设现在有 A,B 两个网站:
- A 网站的域名是
http://img.sky.com - B 网站的域名是
http://nexus.sky.com
在域名商购买的域名 DNS 解析记录中的两条记录将上面两个域名都解析到 42.35.67.88这个 ip 上。
那么常见的 Web 服务器 Nginx 只需使用以下配置即可为不同域名提供不同的应用服务(server模块即为虚拟主机):
1 | server { |
Nginx 如何知道来源的请求访问的域名是哪个呢?
其实,答案很简单,当客户端发起一个 HTTP 请求,比如http://nexus.sky.com,之后会经历以下步骤:
- ① 由 DNS 解析出域名对应的 ip 为
42.35.67.88 ② 客户端向解析出的 IP 发起访问的 HTTP 请求,此 HTTP 请求将携带一个特殊的请求头
Host:1
2GET / HTTP/1.1
Host: nexus.sky.com③ Web 服务器 Nginx 根据请求
Host中指定的域名判断出具体的执行服务,交由对应服务进行处理。
下载与安装
在 Nginx 官网下载地址 选择 Stable version(稳定版本)进行下载即可。
前置工作
安装编译环境
1 | yum -y install gcc gcc-c++ |
安装 pcre (使 Nginx 支持http rewrite 模块)
1 | yum install -y pcre pcre-devel |
安装 openssl-devel(使 Nginx 支持 SSL)
1 | yum install -y openssl openssl-devel |
安装 zlib(使 Nginx 支持 Gzip)
1 | yum install -y zlib zlib-devel gd gd-devel |
创建用户 nginx(选填)
1 | useradd -s /sbin/nologin nginx |
编译安装
Nginx 通过编译安装前,可以指定一些文件存放路径配置及启用或禁用 Nginx 支持的模块,执行以下命令可查看相应的参数选项:1
./configure --help
上述命令输出结果如下: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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172--prefix=PATH set installation prefix
--sbin-path=PATH set nginx binary pathname
--modules-path=PATH set modules path
--conf-path=PATH set nginx.conf pathname
--error-log-path=PATH set error log pathname
--pid-path=PATH set nginx.pid pathname
--lock-path=PATH set nginx.lock pathname
--user=USER set non-privileged user for
worker processes
--group=GROUP set non-privileged group for
worker processes
--build=NAME set build name
--builddir=DIR set build directory
--with-select_module enable select module
--without-select_module disable select module
--with-poll_module enable poll module
--without-poll_module disable poll module
--with-threads enable thread pool support
--with-file-aio enable file AIO support
--with-http_ssl_module enable ngx_http_ssl_module
--with-http_v2_module enable ngx_http_v2_module
--with-http_realip_module enable ngx_http_realip_module
--with-http_addition_module enable ngx_http_addition_module
--with-http_xslt_module enable ngx_http_xslt_module
--with-http_xslt_module=dynamic enable dynamic ngx_http_xslt_module
--with-http_image_filter_module enable ngx_http_image_filter_module
--with-http_image_filter_module=dynamic
enable dynamic ngx_http_image_filter_module
--with-http_geoip_module enable ngx_http_geoip_module
--with-http_geoip_module=dynamic enable dynamic ngx_http_geoip_module
--with-http_sub_module enable ngx_http_sub_module
--with-http_dav_module enable ngx_http_dav_module
--with-http_flv_module enable ngx_http_flv_module
--with-http_mp4_module enable ngx_http_mp4_module
--with-http_gunzip_module enable ngx_http_gunzip_module
--with-http_gzip_static_module enable ngx_http_gzip_static_module
--with-http_auth_request_module enable ngx_http_auth_request_module
--with-http_random_index_module enable ngx_http_random_index_module
--with-http_secure_link_module enable ngx_http_secure_link_module
--with-http_degradation_module enable ngx_http_degradation_module
--with-http_slice_module enable ngx_http_slice_module
--with-http_stub_status_module enable ngx_http_stub_status_module
--without-http_charset_module disable ngx_http_charset_module
--without-http_gzip_module disable ngx_http_gzip_module
--without-http_ssi_module disable ngx_http_ssi_module
--without-http_userid_module disable ngx_http_userid_module
--without-http_access_module disable ngx_http_access_module
--without-http_auth_basic_module disable ngx_http_auth_basic_module
--without-http_mirror_module disable ngx_http_mirror_module
--without-http_autoindex_module disable ngx_http_autoindex_module
--without-http_geo_module disable ngx_http_geo_module
--without-http_map_module disable ngx_http_map_module
--without-http_split_clients_module disable ngx_http_split_clients_module
--without-http_referer_module disable ngx_http_referer_module
--without-http_rewrite_module disable ngx_http_rewrite_module
--without-http_proxy_module disable ngx_http_proxy_module
--without-http_fastcgi_module disable ngx_http_fastcgi_module
--without-http_uwsgi_module disable ngx_http_uwsgi_module
--without-http_scgi_module disable ngx_http_scgi_module
--without-http_grpc_module disable ngx_http_grpc_module
--without-http_memcached_module disable ngx_http_memcached_module
--without-http_limit_conn_module disable ngx_http_limit_conn_module
--without-http_limit_req_module disable ngx_http_limit_req_module
--without-http_empty_gif_module disable ngx_http_empty_gif_module
--without-http_browser_module disable ngx_http_browser_module
--without-http_upstream_hash_module
disable ngx_http_upstream_hash_module
--without-http_upstream_ip_hash_module
disable ngx_http_upstream_ip_hash_module
--without-http_upstream_least_conn_module
disable ngx_http_upstream_least_conn_module
--without-http_upstream_random_module
disable ngx_http_upstream_random_module
--without-http_upstream_keepalive_module
disable ngx_http_upstream_keepalive_module
--without-http_upstream_zone_module
disable ngx_http_upstream_zone_module
--with-http_perl_module enable ngx_http_perl_module
--with-http_perl_module=dynamic enable dynamic ngx_http_perl_module
--with-perl_modules_path=PATH set Perl modules path
--with-perl=PATH set perl binary pathname
--http-log-path=PATH set http access log pathname
--http-client-body-temp-path=PATH set path to store
http client request body temporary files
--http-proxy-temp-path=PATH set path to store
http proxy temporary files
--http-fastcgi-temp-path=PATH set path to store
http fastcgi temporary files
--http-uwsgi-temp-path=PATH set path to store
http uwsgi temporary files
--http-scgi-temp-path=PATH set path to store
http scgi temporary files
--without-http disable HTTP server
--without-http-cache disable HTTP cache
--with-mail enable POP3/IMAP4/SMTP proxy module
--with-mail=dynamic enable dynamic POP3/IMAP4/SMTP proxy module
--with-mail_ssl_module enable ngx_mail_ssl_module
--without-mail_pop3_module disable ngx_mail_pop3_module
--without-mail_imap_module disable ngx_mail_imap_module
--without-mail_smtp_module disable ngx_mail_smtp_module
--with-stream enable TCP/UDP proxy module
--with-stream=dynamic enable dynamic TCP/UDP proxy module
--with-stream_ssl_module enable ngx_stream_ssl_module
--with-stream_realip_module enable ngx_stream_realip_module
--with-stream_geoip_module enable ngx_stream_geoip_module
--with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module
--with-stream_ssl_preread_module enable ngx_stream_ssl_preread_module
--without-stream_limit_conn_module disable ngx_stream_limit_conn_module
--without-stream_access_module disable ngx_stream_access_module
--without-stream_geo_module disable ngx_stream_geo_module
--without-stream_map_module disable ngx_stream_map_module
--without-stream_split_clients_module
disable ngx_stream_split_clients_module
--without-stream_return_module disable ngx_stream_return_module
--without-stream_set_module disable ngx_stream_set_module
--without-stream_upstream_hash_module
disable ngx_stream_upstream_hash_module
--without-stream_upstream_least_conn_module
disable ngx_stream_upstream_least_conn_module
--without-stream_upstream_random_module
disable ngx_stream_upstream_random_module
--without-stream_upstream_zone_module
disable ngx_stream_upstream_zone_module
--with-google_perftools_module enable ngx_google_perftools_module
--with-cpp_test_module enable ngx_cpp_test_module
--add-module=PATH enable external module
--add-dynamic-module=PATH enable dynamic external module
--with-compat dynamic modules compatibility
--with-cc=PATH set C compiler pathname
--with-cpp=PATH set C preprocessor pathname
--with-cc-opt=OPTIONS set additional C compiler options
--with-ld-opt=OPTIONS set additional linker options
--with-cpu-opt=CPU build for the specified CPU, valid values:
pentium, pentiumpro, pentium3, pentium4,
athlon, opteron, sparc32, sparc64, ppc64
--without-pcre disable PCRE library usage
--with-pcre force PCRE library usage
--with-pcre=DIR set path to PCRE library sources
--with-pcre-opt=OPTIONS set additional build options for PCRE
--with-pcre-jit build PCRE with JIT compilation support
--without-pcre2 do not use PCRE2 library
--with-zlib=DIR set path to zlib library sources
--with-zlib-opt=OPTIONS set additional build options for zlib
--with-zlib-asm=CPU use zlib assembler sources optimized
for the specified CPU, valid values:
pentium, pentiumpro
--with-libatomic force libatomic_ops library usage
--with-libatomic=DIR set path to libatomic_ops library sources
--with-openssl=DIR set path to OpenSSL library sources
--with-openssl-opt=OPTIONS set additional build options for OpenSSL
--with-debug enable debug logging
默认的编译配置
若执行./configure命令且不携带任何参数,将书记处以下信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18Configuration summary
+ using system PCRE library
+ OpenSSL library is not used
+ using system zlib library
nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/nginx/sbin/nginx"
nginx modules path: "/usr/local/nginx/modules"
nginx configuration prefix: "/usr/local/nginx/conf"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"
路径说明
| 配置 | 说明 |
|---|---|
/usr/local/nginx |
安装目录前缀 |
/usr/local/nginx/sbin/nginx |
二进制脚本 |
/usr/local/nginx/modules |
模块 |
/usr/local/nginx/conf |
配置文件前缀 |
/usr/local/nginx/conf/nginx.conf |
配置文件绝对路径 |
/usr/local/nginx/logs/nginx.pid |
pid |
/usr/local/nginx/logs/error.log |
错误日志 |
/usr/local/nginx/logs/access.log |
访问日志 |
默认安装的模块
在 Nginx 软件包auto文件夹的option文件中,带 YES 的表示 Nginx 默认安装时候自带的模块:
1 | cat options |grep "YES" |
1 | --with-select_module) EVENT_SELECT=YES ;; |
自定义编译安装
如果不想使用默认的编译配置,我们可以在执行./configure命令时携带相关参数,比如以下配置:
| 参数 | 说明 |
|---|---|
--prefix=/usr/share/nginx |
指定安装路径 |
--conf-path=/etc/nginx/nginx.conf |
指定配置文件路径 |
--http-log-path=/var/log/nginx/access.log |
指定访问日志 |
--error-log-path=/var/log/nginx/error.log |
指定错误日志 |
--lock-path=/var/lock/nginx.lock |
指定 lock 文件 |
--pid-path=/run/nginx.pid |
指定 pid 文件 |
通过yum install -y nginx就使用了如上配置。
当然,一般情况下还是建议自定义安装,其实指定--prefix就好,将 Nginx 单独放在一个目录下,统一管理。
比如,笔者习惯这么做:1
./configure --prefix=/data/nginx
注意,若网站需要支持 HTTPS,编译时需要携带 SSL 模块。1
./configure --prefix=/data/nginx --with-http_ssl_module
如果出现http_ssl_module找不到,需要先安装openssl并指定路径。
1 | ./configure --prefix=/data/nginx --with-http_ssl_module --with-openssl=/usr/bin/openssl |
初始的配置文件
安装完 Nginx 未做任何配置的情况下,配置文件是这样的: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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77# 全局参数设置
#user nobody;
worker_processes 1; # 设置nginx启动进程的数量,一般设置成与逻辑cpu数量相同
#error_log logs/error.log; # 指定错误日志
#error_log logs/error.log notice; # 指定错误日志 notice 级别
#error_log logs/error.log info; # 指定错误日志 info 级别
#pid logs/nginx.pid; # pid 文件路径
events { # 事件配置
worker_connections 1024; # 设置一个进程的最大并发连接数
}
http {
include mime.types; # 关联mime类型,关联资源的媒体类型(不同的媒体类型的打开方式)
default_type application/octet-stream; # 根据文件的后缀来匹配相应的MIME类型,并写入Response header,导致浏览器播放文件而不是下载
# 日志格式
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
# 访问日志存放路径
#access_log logs/access.log main;
sendfile on; # 用于开启文件高效传输模式,一般设置为on,若nginx是用来进行磁盘IO负载应用时,可以设置为off,降低系统负载
#tcp_nopush on; # 减少网络报文段数量,当有数据时,先不着急发送, 确保数据包已经装满数据, 避免了网络拥塞
keepalive_timeout 65; # 设置长连接的超时时间,请求完成之后还要保持连接多久,不是请求时间多久,目的是保持长连接,减少创建连接过程给系统带来的性能损耗,类似于线程池
#gzip on; # 是否开启 gzip 压缩
server {
listen 80; # 设置监听的端口
server_name localhost; # 设置绑定的主机名、域名或ip地址
#charset koi8-r; # 设置编码字符
# 针对于此虚拟主机的访问日志
#access_log logs/host.access.log main;
location / {
root html; # 设置服务器默认网站的根目录位置
index index.html index.htm; # 设置默认打开的文档
}
# 设置错误信息返回页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
简易安装
如果想快速安装,可以通过 rpm 方式处理
1 | wget http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.20.2-1.el7.ngx.x86_64.rpm |
相关命令
进入安装的 Nginxsbin目录,执行nginx脚本:
| 命令 | 说明 |
|---|---|
nginx |
启动 Nginx |
nginx -c /path/to/nginx.conf |
指定配置文件启动 Nginx |
nginx -v |
查看版本 |
nginx -t |
验证配置文件是正确 |
nginx -t -c /path/to/nginx.conf |
测试指定配置文件是否正确 |
nginx -s reload |
重启 Nginx |
nginx -s stop |
快速停止 Nginx |
nginx -s quit |
完整有序的停止 Nginx |
语法规则
Nginx 配置文件为nginx.conf,其语法规则为:
- 由指令及指令块组成
- 指令块以
{}将多条指令组织在一起 - 每条指令以
;结尾,指令与参数及以空格符号分隔 - 部分指令的参数支持正则表达式
include语句允许组合多个配置文件以提升可维护性- 使用
#添加注释 - 使用
$使用变量
user 模块
配置服务器用户(组)的指令是user1
user [user] [group]
各参数说明如下:
- 第一个
user是指令 - 第二个
[user]是指定可以运行 Nginx 的用户 - 第三个
[group]是可选项,指定可以运行 Nginx 的用户组
只有设置的用户或用户组才有权限启动 Nginx 服务进程。
如果未配置(默认)或配置设置为:1
user nobody nobody;
则代表所有用户都可以启动 Nginx 进程。
server 模块
由于 IPV4 地址的数量有限,为了降低 IPV4 地址资源占用,也为了在配置好的服务器部署多个服务,因此经常存在多个主机域名对应着同一个 IP 地址的情况。这时在nginx.conf中就可以按照server_name(对应用户请求中的主机域名)并通过server块来定义虚拟主机,每个server块就是一个虚拟主机,它只处理与之相对应的主机域名请求。
这样, 一台服务器上的 Nginx 就能以不同的方式处理访问不同主机域名的 HTTP 请求了。
监听端口
语法:listen address:port
listen参数决定 Nginx 服务如何监听端口。在listen后可以只加IP地址、端口或主机名,非常灵活,例如:
1 | # 监听 8080 端口 |
匹配主机
语法: server_name name[......]
在开始处理一个 HTTP 请求时,Nginx 会取出header头中的 Host,与每个server中的 server_name进行匹配,以此决定到底由哪一个server块来处理这个请求。有可能一个Host与多个server块中的server_name都匹配,这时就会根据匹配优先级来选择实际处理的server块。
匹配优先级如下:
- 首先选择所有字符串完全匹配的
server_name,如www.test.cm - 其次选择通配符在前面的
server_name,如*.test.cm - 再次选择通配符在后面的
server_name,如www.testweb.* - 最后选择使用正则表达式才匹配的
server_name,如~^\.testweb\.com$
处理请求
语法: location[=|~|~*|^~|@]/uri/{...}
location会尝试根据用户请求中的URI来匹配上面的/uri表达式,若可以匹配,就选择对应location{}块中的配置来处理用户请求。
当然,匹配规则是多样的:
location = /uri:精确匹配,只有完全匹配上/uri的请求才能匹配上location ^~ /uri: 开头对 URL 路径进行前缀匹配,并且在正则之前location ~ pattern:开头表示区分大小写的正则匹配location ~* pattern:开头表示不区分大小写的正则匹配location /uri:不带任何修饰符,也表示前缀匹配,但是在正则匹配之后location /:通用匹配,任何未匹配到其它 location 的请求都会匹配到,相当于switch中的default
注意哦:前缀匹配时,Nginx 不对 url 做编码,因此请求为 /static/20%/aa,可以被规则 ^~ /static/ /aa 匹配到(注意空格)
多 location 匹配优先级
多个 location 配置的情况下匹配顺序如下:
- 首先是精确匹配
= - 其次是前缀匹配
^~ - 之后是按顺序的正则匹配
- 然后是不带任何修饰的前缀匹配
- 最后是交给
/通用匹配 - 当有匹配成功时候,停止匹配,按当前匹配规则处理请求
注意:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。比如在前缀匹配:*location /dir01* 与 *location /dir01/dir02*,如有请求 http://localhost/dir01/dir02/file* 将最终匹配到location /dir01/dir02*
例子,有如下匹配规则:
1 | location = / { |
那么产生的效果如下:
- 访问根目录
/,比如http://localhost/将匹配规则A - 访问
http://localhost/login将匹配规则B - 访问
http://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 中,指定静态资源可以使用root或alias 指令,它们都用于指定服务器上的文件夹路径,但存在一些区别:
root指令用于指定 Nginx 服务器的默认根目录。当请求的 URL 与主机名和端口号匹配时,Nginx 将从该目录开始查找请求的文件或目录alias指令用于将请求的 URL 映射到不同的文件夹路径。与root不同,它允许你使用不同于默认根目录的路径来为特定 URL 提供服务。当请求的 URL 与location指令中设置的路径匹配时,Nginx 将从预设的路径替换掉该部分,并在该路径下查找请求的文件或目录。
假设我们在 Nginx 中配置以下指令:
1 | location /files/ { |
请求 http://example.com/files/image.jpg 时,Nginx 将会从 /var/www/files/image.jpg 路径下查找该文件。
而如果使用alias指令,则可以将location部分匹配到一个不同的文件夹路径:
1 | location /files/ { |
使用以上配置,请求 http://example.com/files/image.jpg 时,Nginx将会从/home/user/images/image.jpg 文件夹下查找该文件。
简而言之,静态资源分别使用root指令和alias指令时:
root指令后的路径将作为前缀,location指令中的路径将作为后缀,两者合并后匹配静态资源alias指令会替换location指令后的路径,之后匹配静态资源
最佳实践
因此,在实际使用中,笔者觉得至少有三个匹配规则需要定义,如下:
1 | server { |
功能特性
Nginx 提供了非常多的功能特性,我们可根据业务架构需要选择合适的功能。
反向代理
Nginx 最常用的功能就是将其作为反向代理服务器。
Nginx 作为反向代理服务器时,可以集成负载均衡特性。
无负载均衡模式
在 Nginx 的配置文件中的http模块的server模块的location指令下使用proxy_pass即可配置简单的反向代理(无负载均衡):1
2
3
4
5
6
7
8
9
10http {
server {
listen 80;
server_name test.leeqingshui.com;
location /api/ {
proxy_pass http://10.211.55.1:8080/api/;
}
}
}
上述配置即代表:访问test.leeqingshui.com的请求将被代理到内网10.211.55.1服务器的8080端口处理。
负载均衡模式
要想让 Nginx 的反向代理支持负责均衡模式,需要proxy_pass指令结合upstream模块使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16http {
upstream testApi {
server 10.211.55.1:8080;
server 10.211.55.2:8080;
}
server {
listen 80;
server_name test.leeqingshui.com;
location /testApi/ {
proxy_pass http://testApi/api/;
}
}
}
上述配置即代表:访问test.leeqingshui.com的请求将被代理到名为testApi的upstream模块下指定的两个服务轮询处理,即由10.211.55.1服务器中8080端口运行的的服务10.211.55.2服务器中8080端口运行的的服务轮询处理请求。
负载均衡策略
Nginx 的负载均衡支持以下策略:
- 轮询(默认)
- 权重
- 随机
- ip hash
- url hash
- 最少连接访问
- 响应时间访问
- 备用
- 不启用
轮询(常用)
默认不加任何参数的负载均衡策略即使用轮询策略,前面示例中的即此策略。
权重(偶用)
若机器性能不同,可以配置权重策略,让性能好的机器处理更多请求,在upstream模块的配置中使用weight属性即可,weight取值越大,对应服务处理的请求越多。1
2
3
4upstream testApi {
server 10.211.55.1:8080 weight=2;
server 10.211.55.2:8080 weight=8;
}
ip_hash(了解)
根据客户端的 ip 地址转发同一台服务器, 可以保持会话。
由于分布式服务基本都支持分布式会话的无状态会话,且考虑到用户使用手机时基站会变,即 IP 会变,因此此策略很少使用。
url_hash(了解)
根据用户访问的 URL 定向转发请求到一台服务器。
适用于定向访问固定资源。
least_conn(了解)
最少连接访问。
fair(了解)
根据后端服务器响应时间转发请求,比如某台服务器请求响应时间短,那么可以配置接收更多请求。
此策略需要 Nginx 安装对应模块,由实际场景中容易造成流量倾斜,打垮机器,极少使用。
备用
upstream的backup属性可以指定某台服务作为备用机,只有当其他服务不可用时才使用此服务。1
2
3
4upstream testApi {
server 10.211.55.1:8080 weight=2 backup;
server 10.211.55.2:8080 weight=8;
}
一般不用,其他服务挂了,备用的服务基本也顶不住。
不启用
upstream的down属性可以指定某台服务不参与负载均衡。1
2
3
4upstream testApi {
server 10.211.55.1:8080 weight=2 down;
server 10.211.55.2:8080 weight=8;
}
重试机制
假设现在 Nginx 存在以下反向代理的负载均衡配置。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16http {
upstream testApi {
server 10.211.55.1:8080;
server 10.211.55.2:8080;
}
server {
listen 80;
server_name test.leeqingshui.com;
location / {
proxy_pass http://testApi;
}
}
}
通过 Nginx 反向代理进行负载均衡的多台后端服务中,若10.211.55.1:8080服务出错或宕机了,应当如何处理?
自然是向另一台正常的后端服务发起重试,那么重试的规则应该是怎样的呢?比如说:
- 什么情况下应当重试?
- 重试的时间限制给多久?
- 重试的次数应限制几次?
这取决于以下参数。
proxy_next_upstream
proxy_next_upstream指令可以指定发起重试的情况,这包括:
off:关闭向下一台服务发起重试操作error:在与服务器建立连接、向其传递请求或读取响应头时发生错误时发起重试操作timeout:在与服务器建立连接、向其传递请求或读取响应头时发生超时时发起重试操作invalid_header:服务器返回一个空的或无效的响应时发起重试操作non_idempotent:通常情况下,如果一个请求被发送到上游服务器(1.9.13),具有非幂等方法(POST、LOCK、PATCH)的请求不会被传递到下一个服务器;明确地启用该选项允许重试此类请求。http_500:HTTP 状态码为 500 时发起重试操作http_502:HTTP 状态码为 502 时发起重试操作http_503:HTTP 状态码为 503 时发起重试操作http_504:HTTP 状态码为 504 时发起重试操作http_403:HTTP 状态码为 403 时发起重试操作http_404:HTTP 状态码为 404 时发起重试操作http_429:HTTP 状态码为 429 时发起重试操作
proxy_next_upstream指令可以同时对以上属性配置多个,默认值为error和timeout。
配置时常用默认值,比如:1
2
3
4location / {
proxy_next_upstream error timeout;
proxy_pass http://testApi;
}
proxy_next_upstream_timeout
proxy_next_upstream_timeout指令可设置一个代理请求在重试时被传递到下一个服务器的时间。
默认取值 0,0 会关闭这个限制。
1 | location / { |
proxy_next_upstream_tries
proxy_next_upstream_tries限制将一个请求传递给下一个服务器的可能尝试次数。
默认取值0,0 会关闭这个限制。
1 | location / { |
upstream 下的重试限制
max_fails
设定在fail_timeout参数设定的持续时间内与服务器通信的失败次数,以认为服务器在同样由fail_timeout参数设定的时间内不可用。
默认情况下,不成功的尝试次数被设置为 1,零值禁止对尝试进行统计。
什么是不成功的重试呢?
由这些指令定义
proxy_next_upstreamfastcgi_next_upstreamuwsgi_next_upstreamscgi_next_upstreammemcached_next_upstreamgrpc_next_upstream
fail_timeout
fail_timeout指令的含义存在两个:
- 设置 Nginx 与服务器通信的不成功尝试的时间,在这个时间段内,服务器被认为不可用
- 服务器将被视为不可用的时间段
默认情况下,该参数被设置为 10 秒
动静分离
Nginx 除了可以对后端应用作反向代理功能外,还可对静态资源进行代理,比如代理服务器上的文件资源:
1 | location /statics_file { |
黑白名单
Nginx 配置黑白名单有好几种方式,这里只介绍常用的两种方法。
allow、deny 指令
deny 和 allow 指令属于ngx_http_access_module,Nginx 默认加载此模块,所以可直接使用。
上述指令使用时可以通过两种方式实现:
- ① 直接配置文件中添加对应配置
- ② 单独管理 IP 白名单文件,Nginx 读取此文件
① 直接配置文件中添加对应配置
1 | # 白名单设置,allow 后面为可访问 IP |
② Nginx 读取单独管理的 IP 白名单文件
在/home/目录下创建whitelist.conf,并写入需要加入白名单的 IP,添加完成后查看如下:
1 | cat /home/whitelist.conf |
1 | # 白名单IP |
最后在 Nginx 配置读取上述文件:
1 | location /{ |
白名单设置完成,黑名单设置类似。
ngx_http_geo_module
默认情况下,一般nginx是有加该模块的,ngx_http_geo_module:官方文档,参数需设置在位置在http模块中。
此模块可设置IP限制,也可设置国家地区限制。位置在server模块外即可。
真实 IP
实际开发中,系统大多需要提供记录用户日志的功能,因此会需要获取客户访问服务器的真实 IP。
我们当然可以在后端代码通过相关工具方法获取来源 IP,但由于使用 Nginx 后,Nginx 进行了一层中转,这将导致后端应用无法获取真实 IP。
为了解决这个问题,可以在 Nginx 配置文件的location模块下配置proxy_set_header X-Forwarded-For $remote_addr;属性来传递客户端 IP。
1 | http { |
防盗链
待补充。
客户端报文限制与缓冲
实际开发中,文件上传是很常见的业务场景,上传文件的大小不仅可以在后端服务作出限制,在 Nginx 中,也可以进行限制。1
client_max_body_size 128m;
日志
指定格式
压缩
Gzip
作用模块
Gzip 可作用以下模块:
httpserverlocation
语法
示例配置如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18http {
gzip on;
gzip_buffers 16 8k;
gzip_comp_level 1;
gzip_http_version 1.1;
gzip_min_length 20;
gzip_proxied off;
gzip_vary off;
gzip_types text/plain application/x-javascript text/css applic ation/xml;
gzip_types
text/xml application/xml application/atom+ xml application/ss + xml application/xhtml+ xml image/svg+ xml text/javascript application/javascript application/x-javascript
text/x-json application/json application/x-web-app-manifest+json
text/Css text/plain text/x-component
font/opentype application/x- font-ttf application/vnd.ms- fontobject
image/x-icon;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
}
gzip
是否开启 gzip 功能,默认关闭。
gzip_buffers
设置用于压缩响应的缓冲区的数量和大小。
默认情况下,缓冲区的大小等于一个内存页。默认值 32 4K 或 16 8K,取决于操作系统,32 位建议前者,64 位建议后者。
gzip_comp_level
压缩等级,可选值 1-9,数字越大压缩比越高对 CPU 消耗越高。
默认值 1,建议设置较小。
gzip_http_version
使用 gzip 的 HTTP 最小版本,默认值 1.1。
gzip_min_length
设置将被 gzip 压缩的响应的最小长度。
默认值 20,长度仅由Content-Length响应报头字段确定。
gzip_proxied
作为反向代理时,针对上游服务器响应返回的头信息启用或禁用压缩:
off:不做限制any:无条件进行压缩auth:若header头包括Authorization,则启用压缩expired:若header头包括Expires,则启用压缩no-cache:若header头包括Cache-Control:no-cache,则启用压缩no-store:若header头包括Cache-Control:no-store,则启用压缩private:若header头包括Cache-Control:private,则启用压缩enables compression if a response header includes the “Cache-Control” field with the “private” parameter;no_last_modified:若header头不包括Last-Modified,则启用压缩no_etag:若header头不包括ETag,则启用压缩
gzip_vary
启用或禁用插入Vary:Accept-Encoding响应头,默认值 off。
gzip_types
gzip_disable
针对哪些浏览器关闭 gzip 功能,配置"MSIE [1-6]\.(?!.*SV1)"代表排除 1-6 版本的 IE 浏览器。
建议不进行配置,少用正则,可以提升性能。
高可用
Nginx 挂了怎么办?
为了使 Nginx 高可用,可以考虑部署 Nginx 集群。
当然,即使部署了 Nginx 集群,在不做任何处理的情况下,客户访问的 Nginx 节点将一直为同一个,如果客户一直访问的 Nginx 节点刚好挂了呢?我们应如何保证客户的请求均匀的请求到不同的 Nginx 节点上?
这需要借助 Keep-alived 的 IP 漂移技术。
Keep-alived
下载与安装
1
yum install -y keepalived
配置
核心配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20global_defs {
router_id LVS_DEVEL
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.16
192.168.200.17
192.168.200.18
}
}
配置修改
第一个安装服务器修改如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22global_defs {
router_id r1
}
# 实例名称
vrrp_instance demoTest {
state MASTER
# 此配置需要和配置的网卡名称对应上
interface eth0
virtual_router_id 51
priority 100
advert_int 1
# 同一组实例相互认证需要
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟的 ip 地址
virtual_ipaddress {
192.168.200.1
}
}
第二个安装服务器修改如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24global_defs {
router_id r2
}
# 实例名称
vrrp_instance demoTest {
# 设置为备用机
state BACKUP
# 此配置需要和配置的网卡名称对应上
interface eth0
virtual_router_id 51
# 权重设置降低
priority 50
advert_int 1
# 同一组实例相互认证需要
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟的 ip 地址
virtual_ipaddress {
192.168.200.1
}
}
注意两个安装的 Keepalived的三个配置:vrrp_instance、virtual_router_id及authentication需要保持一致,否则无法加入到同一组,也就无法生效。
命令
1 | systemctl start keepalived |
心跳脚本
Nginx 挂了,Keepalived 不应该还存活,需要写一个脚本配合使用。
长连接——keepalive
当使用 Nginx 作为反向代理时,为了支持长连接,需要做到两点:
- 客户端长连接:从
client到nginx的连接是长连接 - 服务端长连接:从
nginx到server的连接是长连接
客户端长连接
默认情况下,nginx已经自动开启了对client连接的keep alive支持(需要浏览器 HTTP 请求支持 keep alive),其默认配置在http模块中:1
2
3http {
keepalive_timeout 65;
}
一般场景可以直接使用,但是对于一些比较特殊的场景,还是有必要调优。比如:1
2
3
4
5
6http {
keepalive_time 1h;
keepalive_timeout 65 65;
keepalive_requests 1000;
send_timeout 60;
}
keepalive_time
keepalive_time限制了通过一个保持连接处理请求的最大时间。在达到这个时间后,连接会在后续的请求处理后关闭,默认值1h。
keepalive_timeout
keepalive_timeout可设置两个参数:
- 第一个参数设置了一个超时,在这个超时期间,客户端的保持连接将在服务器端保持开放(默认 65s)。零值将禁用客户端的保持连接
- 可选的第二个参数在
Keep-Alive: timeout=time响应头域中设置一个值(不选不设置,一般不用)
一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,可适当加大。
keepalive_requests
keepalive_requests设置通过一个保持不变的连接可以提供的最大请求数。在发出最大数量的请求后,连接被关闭。默认值 1000。
这个参数的真实含义,是指一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。
大多数情况下,若 QPS 不是很高时,默认值足够使用。但是,对于一些 QPS 比较高(比如超过 10000 QPS,甚至达到 30000、50000 甚至更高) 的场景,默认值就显得太低。
我们简单计算一下,QPS=10000时,客户端每秒发送 10000 个请求(通常建立有多个长连接),每个连接只能最多跑 1000 次请求,意味着平均每秒钟就会有 1000 个长连接因此被nginx关闭。同样意味着为了保持 QPS,客户端不得不每秒中重新新建 1000 个连接。因此,就会发现有大量的 TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。
因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。
send_timeout
send_timeout设置向客户端传输响应的超时。该超时只在两个连续的写操作之间设置,而不是为整个响应的传输设置。若客户端在这个时间内没有收到任何东西,连接就会关闭。默认值 60s。
注意哦:同步的文件传输文件较大时此值应适当调大,不让客户端会一直接受不到响应。
服务端长连接
首先需要配置使用http1.1协议。以便建立更高效的传输,默认使用http1.0,在http1.0中需要配置header才能在Upstream中所配置的上游服务器默认都是用短连接,即每次请求都会在完成之后断开
默认情况下,nginx访问后端都是用的 HTTP1.0 的短连接(),一个请求来了,Nginx 新开一个端口和后端建立连接,后端执行完毕后主动关闭该链接为了让nginx和后端server(nginx 称为 upstream)之间保持长连接,典型设置如下:
相关配置
upstream中配置
keepalive 100;:向上游服务器的保留连接数keepalive_timeout;:连接保留时间keepalive requests;:一个 tcp 连接复用过程中累积可以并发接收的请求个数
server中配置:1
2
3
4# 配置 http 版本号
proxy_http_version 1.1;
# 默认使用 http1.0 协议,需要在 request 中增加 "Connection: keep-alive" header 才能够支持,而 HTTP1.1 默认支持
proxy_set_header Connection;
最佳实践
版本发布缓存
每次项目大版本更新时,为了做好兼容性,防止客户端网页缓存等,可使用一个新网页地址,打个比方:
- 老网页地址,
v1.1.0版本网页访问地址: http://api.dev.com/pageV110 - 新网页地址,
v1.2.0版本网页访问地址: http://api.dev.com/pageV120
那么项目得 Nginx 配置则需要新加一个 v1.2.0 得配置如下:
1 | server { |
那么博主在每次项目发布得时候就需要配合前端发版,配置一个新网页,故产生了这个通用配置得需求,如下:
1 | server { |
在 nginx 配置文件语法中,location 语句可以使用正则表达式,定义 set $s $1 变量,实现了通用配置。
HTTP 强制跳转 HTTPS
当网站开启 HTTPS 后,为了保证安全性,希望将 HTTP 的访问强制跳转为 HTTPS,Nginx 配置 HTTP 强制跳转 HTTPS 存在三种方式:
- ① rewrite 指令
- ② return 指令
- ③ error_page 指令
① rewrite 指令(验证可行)
1 | server { |
② return 指令(未验证)
1 | server { |
③ error_page 指令(未验证)
只允许 HTTP 来访问时,用 HTTP 访问会让 Nginx 报 497 错误,因此可以利用error_page将链接重定向至 HTTPS 上。
1 | server { |
使用error_page指令时,将 http 和 https 的监听配置写在同一个 server 块中,对应的其他配置也需要在该 server 配置块中完成。 需要注意的是,此时需要将error_page指令语句写在最后,否则不能生效。
自签名证书
有时候,我们需要在本地为一个域名颁发 Nginx 证书并配置 443 端口访问,这可以使用自签名证书。以下是步骤:
1. 生成自签名证书
生成私钥(server.key)和 crt 证书(server.crt)
使用 homebrew 安装的 nginx 默认配置文件 nginx.conf 所在目录为/usr/local/etc/nginx
创建存放 SSL 证书相关文件目录
1 | cd /usr/local/etc/nginx |
- 生成 server.key
1 | openssl genrsa -des3 -out server.key 2048 |
以上命令是基于 des3 算法生成的 rsa 私钥,在生成私钥时必须输入至少 4 位的密码
- 生成无密码的 server.key
1 | openssl rsa -in server.key -out server.key |
- 生成 CA 的 crt
1 | openssl req -new -x509 -key server.key -out ca.crt -days 3650 |
- 基于 ca.crt 生成 csr
1 | openssl req -new -key server.key -out server.csr |
命令的执行过程中依次输入国家、省份、城市、公司、部门及邮箱等信息
- 生成 crt(已认证)
1 | openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey server.key -CAcreateserial -out server.crt |
2. 配置 Nginx
编辑你的 Nginx 配置文件,例如 /etc/nginx/sites-available/default 或其他你正在使用的配置文件,添加以下配置:
1 | server { |
3.本地 Host 配置
1 | # 添加本地 https 测试域名 ip 映射 |
4.设置Mac信任自签名证书
在浏览器的地址栏,点击不安全的图标,再点击证书(无效)进 入证书详情
在证书详情对话框中,那个证书的图标是可以拖动的,按住拖动任意文件夹中,会自动保存为一个*.cer文件,双击*.cer文件,会出来一个安装对话框,选择安装到“系统”钥匙串
安装完成后,在系统自带的月匙串访问”应用中,找到我们刚安装好的证书,双击它,进入设置详情,把”信任”-栏,全部改为”始终信任”后关闭对话框
刷新浏览器,即可正常访问。Chrome浏览器中,还需要点击一下” 忽略证书继续前往”的选项
扩展
NginxProxyManager
NginxProxyManager 是一个 Nginx Web 管理软件,能够轻松地转发请求到我们在家里或其他地方运行的服务,包括快速配置免费的 SSL,而不需要对 Nginx 或 Letsencrypt 了解太多。
我们可以通过 Docker 方式安装:1
2
3
4
5
6
7
8
9
10
11
12version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:2.9.22'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
文章信息
| 时间 | 说明 |
|---|---|
| 2020-08-06 | 初稿 |
| 2022-10-05 | 重构 |
| 2022-12-04 | 增加 Keepalived 一节 |