引入

Nginx 是一个轻量级/高性能的反向代理 Web 服务器,由 C 语言写的,速度快、性能优秀。主要功能有反向代理、负载均衡、配置 SSL 证书、防盗链、解决跨域问题、缓存、限流、动静资源分离等等

nginx 作用体现

提升系统整体性能

动静资源分离

一般静态资源文件都放在 Nginx 服务器中,当 Nginx 接收到了获取静态资源文件的请求,就直接在 Nginx 服务器中把放进去的静态资源返回,而不用真正到达后端接口,效率非常高

请求转发分配

所有的请求都经过 Nginx,由 Nginx 决定分发到哪个端口程序上,这样即使后端有很多个 Java 程序,但对于前端来说,是无感知的,好比后台只有一个项目在跑

Keepalive 保活

Nginx 会不断监听后端程序的接口 (健康检查),看该服务是不是在正常运行,万一有一个程序挂了,那么 Nginx 就不会把前端发来的请求转发给这个接口,确保后端服务的高可用性和稳定性

常用指令

location

匹配用户请求中的 URI,优先级为: = > 完整路径 > ^~ > ~~* > 部分起始路径 > /

  • =: 精确匹配,优先级最高。如果找到了这个精确匹配,则停止查找
  • ^~: URI 以某个常规字符串开头,不是正则匹配
  • ~: 区分大小写的正则匹配
  • ~*: 不区分大小写的正则匹配
  • /: 通用匹配, 优先级最低。任何请求都会匹配到这个规则
1
2
3
location 前缀字符串  URL {
[ 配置 ]
}

if/break

判断语句,if 的上下文为 server、location;break 的上下文为 server、location、if

  • =!=: 变量跟字符串的比较
  • ~~*: 变量与正则表达式匹配,区分/不区分 大小写
  • -f!-f: 检查文件是否存在
  • -d!-d: 检查目录是否存在
  • -e!-e: 检查文件、目录或符号链接的存在性
  • -x!-x: 检查可执行文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 如果用户代理 User-Agent 包含"MSIE",rewrite 请求到 /msie/ 目录下。通过正则匹配的捕获可以用 $1 $2 等使用
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
}

# 如果 cookie 匹配正则,设置变量 $id 等于匹配到的正则部分
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
set $id $1;
}

# 如果请求的文件存在,则开启缓存,并通过 break 停止后面的检查
if (-f $request_filename) {
expires max;
break;
}

# 如果请求的文件、目录或符号链接都不存在,则用 rewrite 在 URI 头部添加 /index.php
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$1 break;
}

return

  • 重写的 URL 适用于每个匹配的 server 或 location 的请求
  • 使用标准的 nginx 变量构建重写的 URL
1
2
3
4
5
6
7
8
server {
# 同时用于 HTTP 和 HTTPS 流量
listen 80;
listen 443 ssl;
server_name www.old-name.com;
# 停止处理请求,直接返回 301 (Moved Permanently) 代码和指定的重写过的 URL 到客户端
return 301 $scheme://www.new-name.com$request_uri;
}

rewrite

改变部分或整个用户请求中的 URL

  • 通知客户端,请求的资源已经换地方了。例如网站改版后添加了 www 前缀,通过 rewrite 规则可以将所有请求导向新站点
  • 控制 Nginx 中的处理流程。例如当需要动态生成内容时,将请求转发到应用程序服务器。try_files 指令经常用于这个目的

  • rewrite 指令只能返回代码 301 或 302。要返回其他代码,需要在 rewrite 指令后面包含 return 指令

  • rewrite 指令不一定会暂停 nginx 对请求的处理,因为它不一定会发送重定向到客户端。除非明确指出 (使用 flag 或 URL 的语法) 你希望 nginx 停止处理或发送重定向,否则它将在整个配置中运行,查找在重写模块中定义的指令 (break、if、return、rewrite 和 set),并按顺序处理。如果重写的 URL 与 rewrite 模块中的后续指令匹配,nginx 会对重写的 URL 执行指定的操作 (通常会重新写入)
1
2
3
4
5
6
7
server {
# ...
rewrite ^(/download/.*)/media/(\w+)\.?.*$ $1/mp3/$2.mp3 last;
rewrite ^(/download/.*)/audio/(\w+)\.?.*$ $1/mp3/$2.ra last;
return 403;
# ...
}
  • 它匹配以字符串 /download 开头的 URL,然后用 /mp3/ 替换在路径稍后的某个位置包含的 /media/ 或 /audio/ 目录,并添加适当的文件扩展名 .mp3 或 .ra。$1 和 $2 变量捕获不变的路径元素。例如,/download/cdn-west/media/file1 变为 /download/cdn-west/mp3/file1.mp3。如果文件名上有扩展名(例如.flv),表达式会将其剥离并用.mp3 替换
1
rewrite regex URL [flag];

flag

  • last: 停止处理当前的 ngx_http_rewrite_module 指令集,并开始对匹配更改后的 URI 的新 location 进行搜索 (再从 server 走一遍匹配流程)。此时对于当前 server 或 location 上下文,不再处理 ngx_http_rewrite_module 重写模块的指令
  • break: 停止处理当前的 ngx_http_rewrite_module 指令集
  • redirect: 返回包含 302 代码的临时重定向,在替换字符串不以 “http://”,“https://” 或 “$scheme” 开头时使用
  • permanent: 返回包含 301 代码的永久重定向

try_files

参数是一个或多个文件或目录的列表,以及最后面的 URI 参数

  • Nginx 会按顺序检查文件及目录是否存在 (根据 root 和 alias 指令设置的参数构造完整的文件路径),并用找到的第一个文件提供服务。在元素名后面添加斜杠 / 表示这个是目录。如果文件和目录都不存在,Nginx 会执行内部重定向,跳转到命令的最后一个 uri 参数定义的 URI 中
  • 必须定义一个 location 块捕捉内部重定向。最后一个参数可以是命名过的 location,由初始符号(@)指示
1
2
3
4
5
6
7
location /images/ {
try_files $uri $uri/ /images/default.gif;
}

location = /images/default.gif {
expires 30s;
}
  • 如果客户端请求的文件不存在,Nginx 会响应一个默认的 GIF 文件。假设客户请求 “http://www.domain.com/images/image1.gif”,Nginx 会首先通过用于这个 location 的 root 和 alias 指令,在本地目录中查找这个文件。如果“image1.gif”文件不存在,Nginx 会查找“image1.gif/”目录,如果都不存在,会重定向到“/images/default.gif”。这个值精确匹配后面的 location 指令,因此处理过程停止,Nginx 返回这个文件,并标注其缓存 30 秒

具体应用

反向代理

  1. 正向代理:
  • 防止服务端获取客户端的 ip 地址

  1. 反向代理
  • 防火墙: 应用不想直接暴露给客户端,通过 nginx 过滤掉没有权限或者非法的请求,来保障内部服务器的安全
  • 负载均衡

1
2
3
4
5
6
7
8
9
10
server {
listen 80; // 本机ip下的端口
server_name 1.1.1.1; // 本机ip

location / {
proxy_pass http://a.com:8080/;
root html;
index index.html index.htm;
}
}

负载均衡

当一个应用单位时间内访问量激增,服务器的带宽及性能受到影响,影响大到自身承受能力时,服务器就会宕机崩溃,可以通过 nginx 来转发请求给不同的程序应用来分担服务器压力

常见的负载均衡算法

  1. 轮询算法: 按顺序、平均地把每一条请求分发出去
1
nginx.configupstream backserver { server 192.168.0.1; server 192.168.0.2;}
  1. 权重分配: 权重值高的分配请求也越多
1
nginx.configupstream backserver {server 192.168.0.1 weight=2;server 192.168.0.2 weight=8;}
  1. 按用户端的 ip 进行 hash 运算分配
1
2
3
4
5
upstream backserver {
ip_hash;
server 192.168.0.11:88;
server 192.168.0.13:80;
}

解决跨域问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
location / {
#允许跨域请求的域,* 代表所有
add_header 'Access-Control-Allow-Origin' *;
#允许请求的header
add_header 'Access-Control-Allow-Headers' *;
#允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' 'true';
#允许请求的方法,比如 GET,POST,PUT,DELETE
add_header 'Access-Control-Allow-Methods' *;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://192.168.1.12:8080;
}

配置白名单

可以配置 nginx 的白名单,规定有哪些 ip 可以访问你的服务器,防爬虫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
location / {
deny 192.168.0.1; // 禁止该ip访问 deny all; // 禁止所有
}
}

server {
location / {
if ( $ip_whitelist = 0 ){
return 403; //不在白名单返回 403
}
index index.html;
}
}

适配 PC 与移动端环境

当用户从移动端打开 PC 端 baidu.com 的场景时,将自动跳转指移动端 m.baidu.com,本质上是 Nginx 可以通过内置变量 $http_user_agent ,获取到请求客户端的 userAgent,从而知道当前用户当前终端是移动端还是 PC,进而重定向到 H5 站还是 PC 站

1
2
3
4
5
6
7
8
9
10
11
server {
location / {
//移动、pc设备agent获取
if ($http_user_agent ~* '(Android|webOS|iPhone)') {
set $mobile_request '1';
}
if ($mobile_request = '1') {
rewrite ^.+ http://m.baidu.com;
}
}
}

配置 Gzip

1
2
3
4
5
6
7
8
9
server{
gzip on; //启动
gzip_buffers 32 4K;
gzip_comp_level 6; //压缩级别,1-10,数字越大压缩的越好
gzip_min_length 100; //不压缩临界值,大于100的才压缩,一般不用改
gzip_types application/javascript text/css text/bash;
gzip_disable "MSIE [1-6]."; // IE6对Gzip不友好
gzip_vary on;
}

限流算法

漏桶算法

用户端的大量请求 (突发流量)会进入 nginx 内维护的漏桶中,漏桶会按照已定义的固定的速率给服务器分发请求,正常的请求在服务器处理完后会正常返回给用户端。如果水流过大 (突发流量过大)时,漏桶内的水会溢出,这时 nginx 就把这些溢出的水 (流量)直接丢弃,也就是给用户端返回错误信息

令牌桶算法

用户端发请求给 nginx 时,每个请求都要去 nginx 的令牌桶中取一个令牌,令牌桶的容量可设定。如果某个请求成功取到了令牌,那么 nginx 就会把这个请求转发到服务器上进行处理。如果令牌桶已空,请求在 nginx 令牌桶中取不到令牌,那么 nginx 会直接返回该请求,也就是给用户端返回错误信息

常见问题

vpn 和 服务器代理

  1. vpn 是怎么实现在家也能连到公司内网的
  • vpn 代理是通过隧道技术在公共网络上模拟出一条点到点的逻辑专线,在公网上建立一条专门的隧道,从而达到安全数据传输的目的。需要用户端和服务端都部署和配置专门的 vpn 设备,即 vpn 网关,例如路由器式 vpn、交换机式 vpn
  1. vpn 设计与实现
  • vpn 最主要的技术就是隧道技术,用另一种协议去封装当前正在使用的协议。隧道协议分为第二、第三层隧道协议,第二层隧道协议 (如 L2TP、PPTP、L2F 等) 工作在 OSI 体系结构的第二层 (数据链路层);第三层隧道协议 (如 IPSec、GRE 等) 工作在 OSI 体系结构的第三层 (网络层),这两个协议会在网络层封装完 IP 头后,再封装 IPSec 或者 GRE 头,并进行信息加密,这是代理服务器不会做的。然后再交由网络接口层添加 mac 头再丢到公网中,这样公网就认得你的 ip 并且能找到你公司内网前架设的 vpn 网关
  • vpn 设备还会使用常用加解密技术,例如对称密钥加密和非对称密钥加密组合使用,例如用 https 的 TLS 加密技术
  • 密钥管理技术和身份认证及访问控制技术的支持
  1. 代理服务器和 vpn 有什么不一样
  • 匿名性不同。虽然它们都位于请求的中间,都隐藏了 IP 地址,并且都将信息转发。主要区别在于 vpn 需要隧道过程,该过程建立了到 vpn 服务器的直接且不可穿透的连接。代理只是一个开放的端口,是任何人都可以连接到的单个 IP 地址
  • 安全性不同。vpn 会加密数据信息,代理服务器不会,因此 vpn 更加私有,连接不会被穿透,是一个开在公网上的封闭系统
  • 运行级别不同。vpn 是运行在操作系统上,由操作系统内核封装,重定向请求流量。代理服务器是软件级别,处于用户态
  • 速度不同。vpn 涉及加密解密,数据封装,速度较慢,但因其安全性,价格成本会更高

nginx 配置 https

Nginx 常用来配置 Https 认证,主要有两个步骤: 签署第三方可信任的 SSL 证书 和 配置 HTTPS

签署第三方 SSL

配置 https 要用到私钥 .key 文件和 .crt 证书文件,而申请证书文件的时候要用到 .csr 文件

  1. 配置 https
1
2
3
4
5
6
7
8
9
10
11
server {
#ssl参数
listen 443 ssl; //监听443端口,443端口是https的默认端口。80为http的默认端口
server_name example.com;

#证书文件
ssl_certificate example.com.crt; // 证书的绝对路径

#私钥文件
ssl_certificate_key example.com.key; // 密钥的绝对路径
}