Nginx 相关命令
Nginx 支持很多命令行参数,可以运行 nginx -help
查看,这里说一下最常用的命令
-
启动 nginx:
- 默认配置文件启动:直接命令行输入
nginx
即可 - 指定配置文件启动:
nginx -c your/path/to/config
- 默认配置文件启动:直接命令行输入
-
停止nginx
nginx -s quit
等待当前 nginx 工作进程工作完后停止 nginxnginx -s stop
直接停止 nginx 进程- killall 命令杀死进程
-
重启 nginx:
nginx -s reload
在 nginx 运行过程中,修改配置文件后,运行此命令重启 nginx 让新的配置文件生效 -
测试配置文件是否有语法错误:
nginx -t
-
查看 nginx 编译参数:
nginx -V
展示 Nginx 源码编译时使用的参数配置
Nginx 的基础配置
Nginx 配置文件默认路径一般为 /etc/nginx/nginx.conf
,Nginx 配置文件结构如下图所示:
Nginx 每条配置在官方文档中都叫做指令,下文中也用
指令
这个说法
全局块
全局块中的指令针对于 Nginx 进程本身,控制 Nginx 的基本功能. 常用指令如下:
user nobody; # 进程用户 默认为 nobody
daemon on; # on/off 是否开启守护进程,通俗点说就是是否后台运行(生产环境下一般不需要设置,仅调试时使用)
error_log /var/log/nginx/error.log; # 错误日志存储位置
access_log /var/log/nginx/access.log; # 正常请求日志存储位置
worker_processes 5; # nginx 可以开启的工作进程数量,可以设置为 auto
error_log 和 access_log 也可以在 http 块、server 块、location 块中设置。
Event 块
Event 块中的指令,会影响到 Nginx 性能,这里的指令都是比较偏向底层的指令
events {
use epoll; # 指定 nginx 使用的事件模型;常见的有 select/poll/epoll,具体支持的事件模型类型需要在 Nginx 源码编译前指定
worker_connections 5; # 每个工作进程支持的最大连接数
};
Nginx 的进程模型如下图所示
所以 Nginx 支持的最大连接数为 worker_processes * worker_connections
HTTP 块
HTTP 块中的指令,大多数是针对HTTP服务本身
http {
client_max_body_size 1m; # 请求体最大大小,与请求头中的 content-length 对比,如果请求体大小超过这个限制将会报错
error_page 404 /404.html; # 配置在请求不成功的情况下返回的资源,比如状态码是 40x、50x
keepalive_timeout 75; # http 连接的保活时间
keepalive_requests 100; # 处于 keep-alive 状态的连接的最大数量
log_not_found on; # [on/off] 是否记录 not found 日志
sendfile on; # 使用I/O 操作中的 sendfile(零拷贝)
types {
text/html html;
image/gif gif;
image/jpeg jpg;
} # 设置资源的mime类型
}
Server 块
Server 在nginx的概念中代表着虚拟站点,许多http块中的指令,在server块中也能使用,如果http块中与server块中对同一个指令进行了设置,则以server块中为准;
http {
# xxxxx
server {
client_max_body_size 1m;
error_page 404 /404.html;
keepalive_timeout 75;
# xxxxx 更多 http 块中的配置 xxxxx
listen 80; # 虚拟站点的ip和端口号
server_name www.demo.com; # 虚拟站点的 hostname
}
}
listen 指令
listen 指令只能在 server 块中设置,listen 指令十分复杂以及多样化,指令格式如下:
listen address:port [ default [ backlog=num | rcvbuf=size | sndbuf=size | accept_filter=filter | deferred | bind | ssl ] ]
listen 127.0.0.1:8000; # 监听本机的8000端口
listen 127.0.0.1; # 监听本机的请求
listen 8000; # 监听8000 端口,ip不限
listen *:8000; # 监听8000 端口,ip 不限
listen [::]:80 default ipv6only=on; # 只监听 IPv6的 80端口
Linux 会根据建立连接时获取到的 ip 和端口来和 listen 指令对比。通常情况下,listen 指令只用来指定端口号。
另外,listen 指令可以写多个:
server {
listen 80;
listen 443;
}
这表示 nginx 会监听 80 和 443 端口
server_name 指令
server_name 指令用来与 HTTP 请求头中的 Host
进行对比,并且可以在同一行写多个
server {
listen 80;
server_name www.demo.com www.demo1.com *.demo.com www.demo.* ~^www\d+.demo.com$;
}
*.demo.com
代表匹配以.demo.com
结尾的 Host, www.demo.*
代表匹配以www.demo.
开头的 Host, 以 '~' 开头的则是正则匹配 Host
如果一个 server 块中同时设置了 listen 和 server_name,那么当收到请求时,会首先匹配 listen指令的 IP+端口号,再从匹配到的 server 中匹配 server_name;如果所有 server 的server_name 都未匹配上,则由端口号匹配到的 server 中的第一个来处理。
Location 块
location 在 Nginx 中的概念为虚拟目录,也可以理解为资源位置。同样大部分 server 块中使用的指令,在 Location 块中也能使用(listen 和 server_name 除外),Location 块的作用就是匹配请求的 URI 并声明 URI 对应返回的资源。
server {
listen 80;
server_name www.demo.com;
location /image/logo.svg {
root /data/dist;
}
} # 当请求url为 http://www.demo.com/image/logo.svg 时,返回服务器上路径为 /data/dist/image/logo.svg 的资源
location 指令匹配 URI 的方式有很多种,它们都是针对 URL 的 path 部分进行匹配,location 指令的格式为 location [=|~|~*|^~] /uri/
Location 块的匹配规则
-
正则匹配:正则匹配必须使用
~*
或者~
前缀-
~*
表示不区分大小写的正则匹配# 匹配以 /api 开头的path,不区分大小写 location ~* ^/api { } # http://www.demo.com/api (yes) # http://www.demo.com/API (yes) # http://www.demo.com/api/sdk (yes) # http://www.demo.com/a/api (no)
-
~
表示区分大小写的正则匹配# 匹配以 .gif、.jpg、.jpeg 结尾的path,区分大小写 location ~ .(gif|jpg|jpeg)$ { } # http://www.demo.com/a.gif (yes) # http://www.demo.com/a.GIF (no) # http://www.demo.com/img/a.gif (yes) # http://www.demo.com/a/a.png (no)
-
-
文本匹配:文本匹配也可以添加前缀
-
没有前缀,表示区分大小写匹配以某个字符串开头的 path
# 匹配以 /api 开头的path,不区分大小写 location /api { } # http://www.demo.com/api (yes) # http://www.demo.com/API (yes) # http://www.demo.com/api/sdk (yes) # http://www.demo.com/a/api (no)
-
=
前缀表示精确匹配# 精确匹配 /api location = /api { } # http://www.demo.com/api (yes) # http://www.demo.com/API (no) # http://www.demo.com/api/sdk (no)
-
^~
表示匹配以某个字符串开头的 path,并且不再进行正则匹配# 匹配以 /api 开头的path,不区分大小写(高优先级) location ^~ /api { } # 可 http://www.demo.com/api (yes) # http://www.demo.com/api/sdk (yes) # http://www.demo.com/a/api (no)
-
-
命名 location
-
@
开头,声明一个命名location,可以在内部使用location @error_page { # 声明一个名为 error_page 的 location # [some configure] } location /api { error_page 404 @error_page; } # 以 /api 开头的请求,未找到资源时,则内部重定向到 @error_page
-
在配置了多个 location 块后很容易出现一个请求可以被多个 location 块匹配成功的情况,然而 location 的匹配规则也是有优先级的,优先级从高到低如下:
location = # 精确匹配,优先级最高 OK
location ^~ # 带前缀的文本匹配 ok
location ~ # 区分大小写的正则匹配 ok
location ~* # 不区分大小写的正则匹配 ok
location /path # 没有前缀的文本匹配 wait
location / # 其他location 块都没有匹配到,就会匹配这里,优先级最低
如果多个有同样优先级的 location 块都匹配成功
-
文本匹配:采用规则文本最长的那一个 location 块
# URI 中的 path 为 /api/sdk/get location /api { # 匹配失败 } location /api/sdk { # 匹配成功 }
-
正则匹配:按照在配置文件中出现的位置,取最前面的一个
资源 Location
指明资源目录的方式有两种 root
指令和 alias
指令
举个栗子:现在请求的 url 中的 path 为 /page/log
;静态资源的位置为 /data/dist/page/log/index.html
# root 指令
location /page/log {
root /data/dist;
}
# alias 指令
location /page/log {
alias /data/dist/page/log;
}
root 指令的含义是声明资源的根目录,而 alias
指令的含义是声明资源的虚拟目录。
使用 root 指令声明资源位置,Nginx 在查找资源时会按照 root + path
的路径去服务器上查找。
而使用 alias 声明资源位置,Nginx 查找资源的路径:将请求path中被匹配到的文本部分替换为 alias 路径
# 请求 path 为 /page/log/error/
location /page/log {
alias /data/dist/page/log;
}
# Nginx 返回资源的路径为 /data/dist/page/log/error
需要注意的是,如果 location 的规则是正则匹配,并且想要在 location 块中使用 alias 指令,按照官方文档的说法,这个正则表达式里面必须包含捕获(captures),并且alias 路径中需要用到这些捕获,例:
location ~ ^/image/(.+.(?:gif|jpe?g|png))$ {
alias /data/dist/images/$1;
}
测试下来,个人理解文档中的捕获实际上就是 ()
,一对圆括号里面的子表达式中所匹配到的字符串就是一个捕获,而这些捕获会被以变量的方式注入到location块内部 $1、$2、$3 ...
,如上例中就能捕获到请求中的图片文件名,然后以变量的方式拼接到 alias 路径的后面。
另外当未在请求 path 或者 alias 中指明文件名时,Nginx 会自动寻找路径对应文件夹下的 index.html;当然也可以使用 index 指令指定该目录下的默认资源
location /page/log {
alias /data/dist;
index demo.html
}
# 当请求 path 为 /page/log 时,Nginx 返回的资源是 /data/dist/demo.html
添加响应头
使用 add_header 指令可以添加http 响应头,这个指令在http块 、server 块和 location 块中都可以使用
location ~* .(gif|jpg|png|bmp|ico)$ {
root /data/dist/image/
add_header Cache-Control no-store;
}
语法为 add_header name value
另外 expires 需要单独设置
location ~* .(gif|jpg|png|bmp|ico)$ {
root /data/dist/image/
expires 1d;
}
代理 Location
location 块中使用代理的核心指令就是 proxy_pass
location / {
proxy_pass http://www.baidu.com; # 将请求转发到 http://www.baidu.com
}
proxy_pass 指令的值就是目标服务器的URL,但经过测试,如果是随便乱写的域名,Nginx 会报错
至于报错信息中的upstream 是什么,后面会讲到。但是只要这个域名是真实有效的,或者存在于本机host文件中,就可以生效。
proxy_pass 的路径规则
-
文本规则 location 中块,如果 proxy_pass 的 URL 中没有 path,则直接按原请求的path转发
# 请求 path 为 /page/log/error location /page/log { proxy_pass http://127.0.0.1; } # 请求转发路径为 http://127.0.0.1/page/log/error
-
文本规则 location 块中,如果 proxy_pass 的 URL 中有 path,则与alias在文本location中的规则一样
# 请求 path 为 /page/log/error location /page/log { proxy_pass http://127.0.0.1/dist; } # 请求转发路径为 http://127.0.0.1/dist/error
-
正则匹配规则的 location 块中,proxy_pass 的 URL 中不允许有path,否则,Nginx 会报错。请求会被直接按照原 path 转发到目标服务器
# 请求 path 为 /page/log/error location ~ /page/log { proxy_pass http://127.0.0.1; } # 请求转发路径为 http://127.0.0.1/page/log/error
增加请求头
使用 proxy_set_header指令 可以对请求中的头部进行重定义、或者是增加请求头,再一并发送给被代理的服务器。例:
location ~ /page/log {
proxy_pass http://127.0.0.1;
proxy_set_header X-Real-IP $remote_addr; # $remote_addr --请求发送方IP
proxy_set_header Host $host; # $host 代表请求头中的 host
}
上面的例子中用到了 nginx 内置变量,nginx 内置变量有很多,这里不列举,更多 nginx 内置变量看这里: nginx.org/en/docs/var…
Nginx 实现常见的其他功能
Nignx 的指令非常丰富且复杂,由于篇幅原因文中只能讲一些最基本的指令,达到快速入门并使用的目的。下文中将会介绍一些常见的功能是如何用 Nginx 配置实现的。
负载均衡
上文报错信息中提到了 upstream
,upstream 指令在 Nginx 中的作用是定义一个服务器分组,然后可以在 proxy_pass 中使用它;
upstream backend1 {
server 192.168.10.1:8080 weight=1;
server 192.168.10.2:8080 weight=2;
server 192.168.10.3:8080 weight=3;
}
server {
listen 80;
location / {
proxy_pass http://group1;
}
}
上例中声明了一个名为 backend1 的服务器分组,并在location 中使用,那么此时这个location所在的server接收到的所有请求都会被转发到 backend1 中 的某一个 server 上。weight 代表权重,权重值越高,被分配的请求数量就越多,也就是负载越大。
upsteam 中的 server 还有一些其他参数 ,比如:
upstream backend2 {
server 192.168.10.1:8080 max_conns=100 backup down ;
}
- max_conns:最大连接数
- backup 标记一个server 为备用的server,只有当其他server不可用时,才会使用这个
- down 标记一个server 暂时不可用
Nginx 负载均衡策略
常见的 nginx 负载均衡策略有如下几种:
-
轮询(默认策略): upstream 中的 server 都不加权重(weight),Nginx 依次将请求转发到 server,这种策略的效果等于每个server的weight相同
-
加权轮询:通过添加 weight 参数进行加权处理,权重值越大,服务器越容易被访问,因此,性能好的服务器应适当加大权重值
-
ip_hash:将某个客户端的ip的请求固定转发到同一台机器上
upstream backend3 { ip_hash; server 192.168.10.1:8080; server 192.168.10.2:8080; server 192.168.10.3:8080; } # 优势是:解决了服务端与客户端之间的session共享问题 # 缺点是:无法保证负载均衡,有可能某个服务器的负载很大而其他服务器空闲
-
url_hash:与ip_hash类似,但是这种策略是基于URL的,将某个URL固定转发到某个机器上,主要解决了缓存命中率的问题。
-
least_conn:将请求转发给连接数最少的 server
upstream backend4 { least_conn; server 192.168.10.1:8080; server 192.168.10.2:8080; server 192.168.10.3:8080; }
解决跨域问题
反向代理
Nginx 反向代理常被用来作为解决跨域问题的手段。使用代理的方式解决跨域问题的核心逻辑是让静态资源请求和ajax请求的域名相同。在没有前后端分离的情况下,如下图所示:
此时静态资源和接口服务(动态资源)在同一个Web Server 中,也就是在浏览器中静态资源请求与ajax请求的协议+域名+端口 是完全一样的(同源),不会受到跨域问题的影响;
当前后端分离以后(动静分离):
此时动态资源server和静态资源server 分离,那么就无法让动态资源请求和静态资源请求保持同源,即使将这两个server放到同一个机器上,在不借助其他工具的情况下,是无法让动态资源请求和静态资源请求的url的端口号相同的。
使用 Nginx 反向代理解决跨域问题,如下图所示:
动态资源请求和静态资源请求的目标都是 Nginx 服务,动态资源请求由Nginx 代理转发到真正的目标服务器,Nginx 收到响应以后,再响应给客户端。这样,对于浏览器来说动态资源请求和静态资源请求的目标同源,不受同源策略影响。配置示例如下
server {
listen 80;
error_log /log/error.log;
access_log /log/access.log;
location / { # 响应静态资源
root /data/dist/
}
location ~ ^/api/sdk/ { # 代理 ajax 请求
proxy_pass http://apiserver;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}
正向代理和反向代理的区别
正向代理和反向代理的主要区别是代理服务器的位置靠近哪一边,代理服务靠近客户端就是正向代理,靠近服务端就是反向代理。对于客户端来说能感知到的代理就是正向代理,感知不到的就是反向代理。比如上例中 Nginx 的反向代理,客户端(浏览器)是感知不到动态资源的请求是经过了一次代理的。正向代理的平时也有应用到,比如科学上网、前端本地开发的 webpack devServer。
访问控制
访问控制用到的两个关键指令:deny
和 allow
,这两个指令都是针对 IP 的,在http块 、server 块和 location 块中都可以使用,下面以server块为例
设置黑名单,禁止某些 ip 或者 ip段访问,使用子网掩码指定 ip段
server {
deny 192.168.10.1;
deny 192.168.11.1/24;
}
设置白名单,只允许某些 ip 或者 ip段访问,通过 deny: all;
禁止所有IP访问,再通过 allow 设置白名单。
server {
allow 192.168.10.1;
allow 192.168.11.1/24;
deny all;
}
GZIP
Gzip 是一种比较常见的压缩格式,在 nginx 中使用 gzip指令就可以开启 gzip 压缩,gzip 指令在http块 、server 块和 location 块中都可以使用
server {
gzip on;
gzip_min_length 1k; # 资源大于 1k 才开启压缩(资源大小从header中的content-length获取)
gzip_http_version 1.1; #设置压缩http协议的版本,默认是1.1
gzip_types text/html text/css image/gif font/woff; # 开启压缩的资源的 mime 类型
gzip_comp_level 1; # gzip压缩比,1 压缩比最小处理速度最快,9 压缩比最大但处理最慢(传输快但比较消耗cpu)
}