Skip to content

Nginx 核心知识

一、Nginx 基础

概述

主流 Web 服务器

Nginx、Apache、Tomcat。

Nginx 的优点

2014 发展至今。

  • 高并发,高性能
  • 可扩展性好
  • 高可靠性
  • 热部署
  • BSD 许可证

Nginx 应用场景

  • 静态资源托管
    • 通过本地文件系统提供服务
  • 反向代理服务
    • Nginx 的强大功能
    • 缓存
    • 负载均衡
  • API 服务
    • OpenResty

Nginx 历史背景

  • 互联网的数据量快速增长
    • 互联网的快速普及
    • 全球化
    • 物联网
  • 摩尔定律:性能提升
  • 低效的 Apache
    • 一个连接对应一个进程

Nginx 优点

  • 高并发,高性能
  • 可扩展性好
  • 高可靠性
  • 热部署
  • BSD 许可证

Nginx 组成部分

  • Nginx 二进制可执行文件
    • 由各模块源码编译出的一个文件
  • Nginx.conf 配置文件
    • 控制 Nginx 的行为
  • access.log 访问日志
    • 记录每一条 http 请求信息
  • error.log 错误日志
    • 定位问题

Nginx 版本发布历史

nginx 版本发布情况(mainline)。2002 年开发时间,2004 推出第一个版本。

https://nginx.org/en/download.html。

Nginx 发行版本选择

开源免费的 Nginx 与商业版 Nginx Plus。

开源版:nginx.org

商业版:nginx.com

阿里巴巴的 Tengine,http://tengine.taobao.org/。

Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。

从2011年12月开始,Tengine成为一个开源项目,Tengine团队在积极地开发和维护着它。Tengine团队的核心成员来自于淘宝搜狗等互联网企业。Tengine是社区合作的成果,我们欢迎大家参与其中,贡献自己的力量。

免费 OpenResty 与商业版 OpenRestry

开源 OpenRestry:http://openresty.org

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

商业版 OpenResty:https://openresty.com

技术支持比较好。

编译适合自己的 Nginx

下载 Nginx

https://nginx.org

download

Mainline version、Stable version

下载稳定版。

js
wget https://nginx.org/download/nginx-1.20.1.tar.gz
tar -xzf nginx-1.20.1.tar.gz
cd nginx-1.20.1
wget https://nginx.org/download/nginx-1.20.1.tar.gz
tar -xzf nginx-1.20.1.tar.gz
cd nginx-1.20.1

目录介绍

  • auto
    • cc 编译
    • lib
    • os
    • types
  • CHANGES:Nginx 每个版本中提供提供的特性和修复的 BUG
  • CHANGES.ru:俄罗斯版本的 CHANGEES 文件,作者是俄罗斯的
  • conf 示例文件
  • configure:脚本,生成中间文件,执行编译前的必备动作
  • contrib:提供脚本和 VIM 工具
    • cp -r contrib/vim/* ~/.vim/ 可以使 vim 支持 nginx 语法
  • html:提供标准 HTML 文件,500 错误文件与默认 nginx 欢迎界面
  • main:linux 对 nginx 的帮助文件
  • src:Nginx 源代码

Configure

js
./configure --help | more // 查看帮助文件
./configure --help | more // 查看帮助文件

默认参数编译

js
./configure --prefix=/data/nginx
./configure --prefix=/data/nginx

中间文件介绍

objs

  • ngx_modules.c:决定 nginx 编译时有哪些模块会被编译进 nginx

  • src

编译

js
make // 编译
make // 编译

编译结束后,会在 objs 目录下生成大量二进制文件。

c 语言编译时生成的所有的中间文件都会放到 src 目录

nginx 版本升级时,需要将 objs 目录下的文件拷贝到安装目录中。

如果我们使用动态模块,动态模块会生成 so 动态文件,也会放到 objs 目录下。

安装

js
make install // 首次安装时可以使用此命令
make install // 首次安装时可以使用此命令

安装完成后可以去指定目录查看。

sbin:nginx 二进制文件

config:和源代码中配置文件相同,决定 nginx 功能文件

logs:日志文件

配置文件的通用语法介绍

基础语法

  • 配置文件由指令与指令块组成
  • 每条指令以 ;分号结尾,指令与参数间以空格符号分割
  • 指令块以 {} 大括号将多条指令组织在一起
  • include 语句允许组合多个配置文件以提升可维护性
  • 使用 # 符号添加注释,提高可读性
  • 使用 $ 符号使用变量
  • 部分指令的参数支持正则表达式

配置参数:时间单位

js
ms milliseconds
d days
s seconds
w weeks
m minutes
M months, 30 days
h hours
y years, 365 days
ms milliseconds
d days
s seconds
w weeks
m minutes
M months, 30 days
h hours
y years, 365 days

配置参数:空间单位

js
bytes
k/K kilobytes
m/M megabytes
g/G gigabytes
bytes
k/K kilobytes
m/M megabytes
g/G gigabytes

http 配置的指令块

http:表示内部所有执行由 http 解析并执行

server:定义域名或者一组域名

location:url 表达式

upstream:上游服务,nginx 需要与 tomcat 等其他服务交互时,我们可以定义 upstream

命令行:重载、热部署、日志切割

linux 命令行

  • 格式:nginx -s reload
  • 帮助:-? -h
  • 使用指定的配置文件:-c
  • 指定配置指令:-g
  • 指定运行目录:-p
  • 发送信号:-s
    • 立刻停止服务:stop
    • 优雅的停止服务:quit
    • 重载配置文件:reload
    • 重新开始记录日志文件:reopen
  • 测试配置文件是否有语法错误:-t、-T
  • 打印 nginx 的版本信息、编译信息等:-v、-V

重载配置文件

js
./nginx -s reload // 不停止服务的情况下,重新载入配置文件
./nginx -s reload // 不停止服务的情况下,重新载入配置文件

热部署

js
ps -ef | grep nginx // 查看进程状况
ps -ef | grep nginx // 查看进程状况
js
// 备份现有二进制文件
cp nginx nginx.old

// 替换正在运行的二进制文件
cp -r nginx /data/nginx/sbin/ -f 

// 向 master 进程发送信号
kill -USR2 13195

// 向老的进程发送关闭信号(关闭 work 进程,老的 master 进程还可以重新拉起 worker 进程)
kill -WINCH 13195
// 备份现有二进制文件
cp nginx nginx.old

// 替换正在运行的二进制文件
cp -r nginx /data/nginx/sbin/ -f 

// 向 master 进程发送信号
kill -USR2 13195

// 向老的进程发送关闭信号(关闭 work 进程,老的 master 进程还可以重新拉起 worker 进程)
kill -WINCH 13195

日志切割

js
mv blog.log blog.bak.log

./nginx -s reopen
mv blog.log blog.bak.log

./nginx -s reopen
js
// bash 脚本

crontab -l
// bash 脚本

crontab -l

搭建静态资源 Web 服务器

ngx_http_gzip_module

  • Embeded Variables
    • $gzip_ratio 表示当时使用的压缩比率

autoindex module

当我们访问以 “/” 结尾的 url 时,当我们对应到这个目录中,显示这个目录的结构

ngx_http_core_module

  • Embeded Variables
    • $limit_rate 限制响应速度
    • $content_length 记录请求头部长度
    • 。。。
nginx
server {
  listen 8080;
  server_name test.yueluo.club;
  
  gzip on; # 压缩
  gzip_min_length 1; # 小于多少字节就不再压缩
  gzip_comp_level 2; # 压缩级别
  gzip_types text/plain application/X-javascript text/css image/jpeg; # 压缩类型
	
  location / {
    alias www/project/; 
    # / 代表所有请求
    # 通常使用 alias,root 会携带路径
    # 这里就是说 project 目录下后面的路径和 url 是一一对应的
    autoindex on; # 显示文件结构,当路径是一个文件时
    
    set $limit_rate 1k; # 限制 nginx 向客户端浏览器发送响应的速度,每秒传输多少字节到浏览器中
    
  }
}
server {
  listen 8080;
  server_name test.yueluo.club;
  
  gzip on; # 压缩
  gzip_min_length 1; # 小于多少字节就不再压缩
  gzip_comp_level 2; # 压缩级别
  gzip_types text/plain application/X-javascript text/css image/jpeg; # 压缩类型
	
  location / {
    alias www/project/; 
    # / 代表所有请求
    # 通常使用 alias,root 会携带路径
    # 这里就是说 project 目录下后面的路径和 url 是一一对应的
    autoindex on; # 显示文件结构,当路径是一个文件时
    
    set $limit_rate 1k; # 限制 nginx 向客户端浏览器发送响应的速度,每秒传输多少字节到浏览器中
    
  }
}

日志相关

nginx
http {
  # 命名 main,方便对不同的 url 或者域名做不同的处理
  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"'
}
http {
  # 命名 main,方便对不同的 url 或者域名做不同的处理
  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"'
}
nginx
server {
  listen 8080;
  server_name test.yueluo.club;
  
  access_log data/logs/test/access.log main; # 使用 main 的日志格式,定义输出目录
  
  gzip on; # 压缩
  gzip_min_length 1; # 小于多少字节就不再压缩
  gzip_comp_level 2; # 压缩级别
  gzip_types text/plain application/X-javascript text/css image/jpeg; # 压缩类型
	
  location / {
    alias www/project/; 
    # / 代表所有请求
    # 通常使用 alias,root 会携带路径
    # 这里就是说 project 目录下后面的路径和 url 是一一对应的
    autoindex on; # 显示文件结构,当路径是一个文件时
    
    set $limit_rate 1k; # 限制 nginx 向客户端浏览器发送响应的速度,每秒传输多少字节到浏览器中
    
  }
}
server {
  listen 8080;
  server_name test.yueluo.club;
  
  access_log data/logs/test/access.log main; # 使用 main 的日志格式,定义输出目录
  
  gzip on; # 压缩
  gzip_min_length 1; # 小于多少字节就不再压缩
  gzip_comp_level 2; # 压缩级别
  gzip_types text/plain application/X-javascript text/css image/jpeg; # 压缩类型
	
  location / {
    alias www/project/; 
    # / 代表所有请求
    # 通常使用 alias,root 会携带路径
    # 这里就是说 project 目录下后面的路径和 url 是一一对应的
    autoindex on; # 显示文件结构,当路径是一个文件时
    
    set $limit_rate 1k; # 限制 nginx 向客户端浏览器发送响应的速度,每秒传输多少字节到浏览器中
    
  }
}

搭建具备缓存功能的反向代理服务

listen 8080

listen 127.0.0.1:8080 表示只能本机访问 8080 端口

nginx
server {
  listen 127.0.0.1:8080;
}
server {
  listen 127.0.0.1:8080;
}

搭建反向代理服务

ngx_http_proxy_module

  • proxy_cache nginx 性能领先于上游服务器性能,使用 cache 对小的站点会有非常大的性能提升
nginx
http {
  # 缓存文件相关参数配置
  proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size:10g inactive=60m use_temp_path=off; 
}
http {
  # 缓存文件相关参数配置
  proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size:10g inactive=60m use_temp_path=off; 
}
nginx
upstream local {
  server 127.0.0.1:8080;
}

server {
  listen 80;
  server_name test.yueluo.club;
  
  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwared_for;
    
    proxy_cache my_cache;
    proxy_cache_key $host$uri$is_args$args;
    proxy_cache_valid 200 304 302 1d;
    
    proxy_pass http://local;
  }  
}
upstream local {
  server 127.0.0.1:8080;
}

server {
  listen 80;
  server_name test.yueluo.club;
  
  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwared_for;
    
    proxy_cache my_cache;
    proxy_cache_key $host$uri$is_args$args;
    proxy_cache_valid 200 304 302 1d;
    
    proxy_pass http://local;
  }  
}

GoAccess 实现日志可视化

https://goaccess.io

js
goaccess access.log -o report.html --log-format=COMBINED
goaccess access.log -o report.html --log-format=COMBINED
js
goaccess access.log -o ../html/report.html --real-time-html --time-format='%H:%M:%S' --date-format='%d/%b/%Y' --log-format=COMBINED
goaccess access.log -o ../html/report.html --real-time-html --time-format='%H:%M:%S' --date-format='%d/%b/%Y' --log-format=COMBINED
nginx
server {
	access_log logs/access.log main;
  
  location /report.html {
    alias /data/log/blog/report.html
  }
}
server {
	access_log logs/access.log main;
  
  location /report.html {
    alias /data/log/blog/report.html
  }
}

从网络原理来看 SSL 安全协议

对称加密与非对称加密

对称加密

非对称加密

SSL 如何保证公信力

PKI 公钥基础设施

证书类型

证书链

SSL 协议握手的 Nginx 性能瓶颈

TLS 通讯过程

Nginx 握手性能

Nginx 数据加密性能

Nginx 综合性能

免费 SSL 证书实现一个站点

jsx
yum install python2-certbot-nginx 

yum install certbot // 最新
yum install python2-certbot-nginx 

yum install certbot // 最新
js
certbot --nginx --nginx-server-root=/etc/nginx/conf/ -d music.yueluo.club
certbot --nginx --nginx-server-root=/etc/nginx/conf/ -d music.yueluo.club

基于 OpenResty 实现简单服务

下载 OpenResty

https://openresty.org/cn/

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

下载页面:https://openresty.org/cn/download.html

通常我们不会下载二进制版本,我们需要下载源码,复制其下载链接

js
wget https://openresty.org/download/openresty-1.19.3.2.tar.gz
wget https://openresty.org/download/openresty-1.19.3.2.tar.gz

分析目录结构

  • build 编译生成的目标文件

  • bundle 模块文件,基于 nginx 二次开发

    • nginx c 模块
    • lua 模块
  • configure

    • ./configure --help 帮助命令
  • patches

  • util

编译

js
./configure

make
./configure

make

添加 lua 代码

js
vim nginx.conf
vim nginx.conf
nginx
server {
  location /lua {
    default_type text/html;
    # http 请求内容生成阶段用 lua 处理
    content_by_lua 'ngx.say("User-Agent: ", ngx.req.get_headers()["User-Agent"])'
  }
  
  location / {
    alias html/music/;
  }
}
server {
  location /lua {
    default_type text/html;
    # http 请求内容生成阶段用 lua 处理
    content_by_lua 'ngx.say("User-Agent: ", ngx.req.get_headers()["User-Agent"])'
  }
  
  location / {
    alias html/music/;
  }
}

运行

js
nginx -s reload
nginx -s reload

二、Nginx 架构基础

Nginx 的请求处理流程

Nginx 的进程结构

  • 单进程结构
    • 不适合生产环境,只适合开发,调试
  • 多进程结构

多进程模型

  • nginx 需要保持高可用性和高可靠性,所以不能使用线程,线程之间存在共享空间
  • master 进程中通常不会加入第三方模块代码
    • master 主要是用来做进程管理,处理 worker 进程
    • 缓存需要被 worker 进程使用,也要被 Cache manager 进程使用
  • woker 进程为什么很多
    • nginx 采用事件驱动模型之后,希望每个 worker 进程从头到尾占用一个 CPU,可以更好地使用 CPU 缓存
js
ps -ef | grep nginx // 查看 nginx 进程
ps -ef | grep nginx // 查看 nginx 进程
js
nginx -s reload // 重载 nginx 服务,会重新开启新的 worker 子进程和 Cache manager 进程
kill -SIGHUP 9170 // SIGHUP 信号和 reload 作用是一致的
nginx -s reload // 重载 nginx 服务,会重新开启新的 worker 子进程和 Cache manager 进程
kill -SIGHUP 9170 // SIGHUP 信号和 reload 作用是一致的
js
kill -SIGTERM 9862 // 退出子进程,子进程会通知父进程,父进程会重启一个子进程
kill -SIGTERM 9862 // 退出子进程,子进程会通知父进程,父进程会重启一个子进程

信号管理 Nginx 父进程

Master 进程

  • TERM 立刻停止 Nginx 进程

  • QUIT 优雅的停止 Nginx 进程

  • HUP 重载配置文件

  • USR1 重新打开日志文件,进行日志文件切割

  • 以上 4 个命令可以通过命令行向 master 进程发送

  • USR2

  • WINCH

  • 以上 2 个命令只能通过 kill linux 命令行向 master 进程发送

调用 Nginx 命令行发送相应命令本质是也是发送信号指令。

reload 重载配置文件的原理

老进程由于异常导致无法正常关闭,这时可以配置 worker_shutdonwn_timeout 最长等待时间,master 开启子进程之后会开启定时器,如果定时时间到达之后,老的 workder 进程还没有关闭,会强制关闭。

热升级的完整流程

替换 Nginx 文件:替换 bundary 文件。

优雅地关闭 worker 进程

js
nginx -s reload // 优雅关闭
nginx -s stop // 立即关闭
nginx -s reload // 优雅关闭
nginx -s stop // 立即关闭

优雅关闭是对 worker 进程 的 http 请求 而言的。

当老进程进行 WebSocket 通信时,或者进行 TCP 请求时,nginx 不知道什么时候应该关闭。

网络收发与 Nginx 事件间的对应关系

Nginx 是一个事件驱动的框架,这里的事件指的是网络事件。

网络传输

TCP 与报文

TCP 协议与非阻塞接口

事件收集、分发器,我们会定义每个事件对应的消费者。

Nginx 的事件驱动模型

epoll 的优势及原理

Nginx 的事件切换

同步,异步、阻塞与非阻塞

阻塞和非阻塞主要是指操作系统或者底层 C 库提供的方法或者系统调用,调用方法是否会导致进程进程 sleep 状态。

阻塞调用

非阻塞调用

非阻塞调用下的同步与异步

Nginx 的模块

https://nginx.org/en/docs 官方文档。

我们可以从官方文档或者编译出的 nginx 源码来查看具体使用方式。

Nginx 模块定义:

  • ngx_module_t 是每一个模块都具备的
    • type 定义模块类型

Nginx 模块的分类

  • ngx_conf_module:解析 Nginx conf 文件

  • 核心模块

    • http
      • ngx_http_core_module
      • 请求过滤模块
      • 响应过滤模块
    • cvcnts
    • strcam

Nginx 如何通过连接池处理网络请求

。。。。

三、HTTP 模块

冲突的配置指令以谁为准

配置块嵌套

nginx
main
http {
  upstream {...}
  split_clients {...}
  map {}
  geo {}
  
  server {
    if () {}
    location {
      limit_except {...}
    }
    location {
      location {
        
      }
    }
  }
  
  server {
    
  }
}
main
http {
  upstream {...}
  split_clients {...}
  map {}
  geo {}
  
  server {
    if () {}
    location {
      limit_except {...}
    }
    location {
      location {
        
      }
    }
  }
  
  server {
    
  }
}

指令的 Context

指令的合并

存储值的指令继承规则:向上覆盖

HTTP 模块合并配置的实现

并不是所有的指令合并都遵循向上覆盖的原则,我们还需要注意以下几点。

Listen 指令的用法

处理 HTTP 请求头部的流程

接收请求事件模块

接收请求 HTTP 模块

Nginx 中的正则表达式

可以使用 pcre test 测试正则表达式。

如何找到处理请求的 server 指令

server_name 指令

指令后可以跟多个域名,第 1 个是主域名。

js
Syntax: server_name_in_direct on | off;
Default: server_name_in_redirect off;
Context: http,server,location
Syntax: server_name_in_direct on | off;
Default: server_name_in_redirect off;
Context: http,server,location

*泛域名:仅支持在最前或者最后

js
例如:server_name *.yueluo.club
例如:server_name *.yueluo.club

正则表达式:加 ~ 前缀

js
server_name www.yueluo.club ~^www\d+\.yueluo\.club$;
server_name www.yueluo.club ~^www\d+\.yueluo\.club$;

用正则表达式创建变量:用小括号

nginx
server {
    server_name ~^(www\.)?(.+)S;
    location / { root /sites/$2; }
}

server {
    server_name ~^(www\.)?(?<domain>.+)S;
    location / { root /sites/$domain; }
}
server {
    server_name ~^(www\.)?(.+)S;
    location / { root /sites/$2; }
}

server {
    server_name ~^(www\.)?(?<domain>.+)S;
    location / { root /sites/$domain; }
}

其他

  • .yueluo.club 可以匹配 yueluo.club *.yueluo.club
  • _匹配所有
    • yueluo.club_
  • “” 匹配没有传递 Host 头部

Server 匹配顺序

  • 精确匹配

  • * 在前的泛域名

  • * 在后的泛域名

  • 按文件中的顺序匹配正则表达式域名

  • default server

    • 第 1 个
    • listen 指定 default

HTTP 请求的 11 个阶段

postread 阶段

获取真实客户端地址的 realip 模块

  • TCP 连接四元组(src ip,src port,dst ip,dst port)
  • HTTP 头部 X-Forwarded-For 用于传递 IP
  • HTTP 头部 X-Real-IP 用户传递用户 IP
  • 网络中存在许多反向代理

我们可以根据 X-Forwarded-For 获取到真实 IP,获取到真实 IP 之后如何使用?

答案是基于变量。像 binary_remote_addr、remote_addr 这样的变量,其值就是真实的 IP,这样做连接限制(limit_conn 模块)才有意义。

realip 模块

realip 模块默认不会编译进 Nginx

  • 通过 --with-http_realip_module 启用功能

功能

  • 修改客户端地址

变量

  • realip_remote_addr
  • realip_remote_port

指令

  • set_real_ip_from
  • real_ip_header
  • real_ip_Rrcursive
nginx
server {
    server_name realip.yueluo.club;
    
    error_log data/logs/realip/error.log dubug;
    
    set_real_ip_from xxx.xx.xxx.xxx;
    # real_ip_header X-Real_IP;
    real_ip_recursive off; # 环回地址
    # real_ip_reucrsize on;
    real_ip_header X-Forwared-For;
    	
    location / {
        return 200 "Client real ip: $remote_addr\n";
    }
}
server {
    server_name realip.yueluo.club;
    
    error_log data/logs/realip/error.log dubug;
    
    set_real_ip_from xxx.xx.xxx.xxx;
    # real_ip_header X-Real_IP;
    real_ip_recursive off; # 环回地址
    # real_ip_reucrsize on;
    real_ip_header X-Forwared-For;
    	
    location / {
        return 200 "Client real ip: $remote_addr\n";
    }
}

rewrite 阶段

return 指令

server 与 location 块下的 return 指令关系?

return 与 error_page 指令的关系?

nginx
server {
  server_name return.yueluo.club;
  listen 8080;
    
  root html/;
  error_page 404 /403.html;
  return 403;
    
  location / {
  	return 404 "find nothing!";
  }
}
server {
  server_name return.yueluo.club;
  listen 8080;
    
  root html/;
  error_page 404 /403.html;
  return 403;
    
  location / {
  	return 404 "find nothing!";
  }
}

return 是 rewrite 提供的指令。

rewrite 指令

nginx
server {
  root html/;

  location /first {
    rewrite /first(.*)/second$1 last;
    return 200 "first!";
  }

  location /second {
    rewrite /second(.*)/third$1 break;
    return 200 "second!";
  }

  location /thrid {
    return 200 "third!";
  }
}
server {
  root html/;

  location /first {
    rewrite /first(.*)/second$1 last;
    return 200 "first!";
  }

  location /second {
    rewrite /second(.*)/third$1 break;
    return 200 "second!";
  }

  location /thrid {
    return 200 "third!";
  }
}
nginx
server {
  location /redirect1 {
    rewrite /redirect(.*)$1 permanent;
  }
  
  location /redirect2 {
    rewrite /redirect2(.*)$1 redirect;
  }
  
  location /redirect3 {
    rewrite /redirect3(.*) http://rewrite.yueluo.club$1;
  }
  
  location /redirect4 {
    rewrite /redirect4(.*) http://rewrite.yueluo.club$1 permanent;
  }
}
server {
  location /redirect1 {
    rewrite /redirect(.*)$1 permanent;
  }
  
  location /redirect2 {
    rewrite /redirect2(.*)$1 redirect;
  }
  
  location /redirect3 {
    rewrite /redirect3(.*) http://rewrite.yueluo.club$1;
  }
  
  location /redirect4 {
    rewrite /redirect4(.*) http://rewrite.yueluo.club$1 permanent;
  }
}

rewrite_on 指令默认是不打开的,如果打开会记录到 error.log 日志中。

条件判断 if

if 指令的条件表达式

  • 检查变量为空或者值是否为 0,直接使用
  • 将变量与字符串做匹配,使用 = 或者 !=
  • 将变量与正则表达式做匹配
    • 大小写敏感,~ 或者 !~
    • 大小写不敏感,~* 或者 !~*,忽略大小写
  • 检查文件是否存在,使用 -f 或者 !-f
  • 检查目录是否存在,使用 -d 或者 !-d
  • 检查文件、目录、软链接是否存在,使用 -e 或者 !-e
  • 检查是否为可执行文件,使用 -x 或者 !-x
nginx
if ($http_user_agent ~ MSIE) {
  rewrite ^(.*)$ /mise/$1 break;
}

if ($http_cookie ~* "id=([^;]+)()?:;|$) {
	set $id $1;
}

if ($request_method = POST) {
  return 405;
}

if ($slow) {
  limit_rate 10k;
}

if ($invalid_referer) {
  return 403;
}
if ($http_user_agent ~ MSIE) {
  rewrite ^(.*)$ /mise/$1 break;
}

if ($http_cookie ~* "id=([^;]+)()?:;|$) {
	set $id $1;
}

if ($request_method = POST) {
  return 405;
}

if ($slow) {
  limit_rate 10k;
}

if ($invalid_referer) {
  return 403;
}

find_config 阶段

location 指令

location 匹配规则:仅匹配 URI,忽略参数

  • 合并连续的 / 符号
    • merge_slashes on
  • 前缀字符串
    • 常规
    • =: 精确匹配
    • ^~:匹配上后则不再进行正则表达式匹配
  • 用于内部跳转的命名 location
    • @
  • 正则表达式
    • ~:大小写敏感的正则匹配
    • ~*:忽略大小写的正则匹配

location 匹配顺序

preaccess 阶段

limit_conn

限制每个客户端的并发连接数

步骤:

  • 定义共享内存(包括大小),以及 key 关键字
js
Syntax: limit_conn_zone key zone=name:size; // 共享内存大小
Default: -
Context: http
Syntax: limit_conn_zone key zone=name:size; // 共享内存大小
Default: -
Context: http
  • 限制并发连接数
js
Syntax: limit_conn zone number;
Default: -
Context: http, server, location
Syntax: limit_conn zone number;
Default: -
Context: http, server, location
  • 限制发生时的日志级别
js
Syntax: limit_conn_log_level info | notice | warn | error;
Default: limit_conn_log_level error;
Context: http, server, location
Syntax: limit_conn_log_level info | notice | warn | error;
Default: limit_conn_log_level error;
Context: http, server, location
  • 限制发生时向客户端返回的错误码
js
Syntax: limit_conn_status code;
Default: limit_conn_status 503;
Context: http, server, location
Syntax: limit_conn_status code;
Default: limit_conn_status 503;
Context: http, server, location
nginx
limit_conn_zone $binary_remote_addr zone=addr:10m;
# limit_conn_zone $binary_remote_addr zone=one: 10m rate=3r/s;

server {
  server_name limit.yueluo.club;
  root html/;
  error_log logs/myerror.log info;
  
  location / {
    limit_conn_status 500;
    limit_conn_log_level warn;
    limit_rate 50;
    limit_conn addr 1;
    # limit_req zone=one brust=1 nodelay;
    # limit_req zone=one;
  }
}
limit_conn_zone $binary_remote_addr zone=addr:10m;
# limit_conn_zone $binary_remote_addr zone=one: 10m rate=3r/s;

server {
  server_name limit.yueluo.club;
  root html/;
  error_log logs/myerror.log info;
  
  location / {
    limit_conn_status 500;
    limit_conn_log_level warn;
    limit_rate 50;
    limit_conn addr 1;
    # limit_req zone=one brust=1 nodelay;
    # limit_req zone=one;
  }
}

limit_req

限制每个客户端的每秒处理请求数

步骤:

  • 定义共享内存(包括大小),以及 key 关键字和限制速率
js
Syntax: limit_req_zone key zone=name:size rate=rate;
Default: _;
Context: http

// rate 单位为 r/s 或者 r/m
Syntax: limit_req_zone key zone=name:size rate=rate;
Default: _;
Context: http

// rate 单位为 r/s 或者 r/m
  • 限制并发连接数
js
Syntax: limit_req zone=name [brust=number][nodelay];
Default: _
Context: http,server,location

// brust 默认为 0
// nodelay,对 burst 中的请求不再采用延时处理的做法,而是立即处理
Syntax: limit_req zone=name [brust=number][nodelay];
Default: _
Context: http,server,location

// brust 默认为 0
// nodelay,对 burst 中的请求不再采用延时处理的做法,而是立即处理
  • 限制发生时的日志级别
js
Syntax: limit_req_log_level info | notice | warn | error;
Default: limit_req_log_level error;
Context: http, server, location
Syntax: limit_req_log_level info | notice | warn | error;
Default: limit_req_log_level error;
Context: http, server, location
  • 限制发生时向客户端返回的错误码
js
Syntax: limit_red_status code;
Default: limit_red_status 503;
Context: http, server, location
Syntax: limit_red_status code;
Default: limit_red_status 503;
Context: http, server, location

limit_req 与 limit_conn 配置同时生效时,那个有效?

nodelay 添加与否,有什么不同?

nginx
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn_zone $binary_remote_addr zone=one: 10m rate=2r/m;

server {
  server_name limit.yueluo.club;
  root html/;
  error_log logs/myerror.log info;
  
  location / {
    limit_conn_status 500;
    limit_conn_log_level warn;
    # limit_rate 50;
    # limit_conn addr 1;
    limit_req zone=one brust=1 nodelay;
    limit_req zone=one;
  }
}
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn_zone $binary_remote_addr zone=one: 10m rate=2r/m;

server {
  server_name limit.yueluo.club;
  root html/;
  error_log logs/myerror.log info;
  
  location / {
    limit_conn_status 500;
    limit_conn_log_level warn;
    # limit_rate 50;
    # limit_conn addr 1;
    limit_req zone=one brust=1 nodelay;
    limit_req zone=one;
  }
}

access 阶段

nginx_http_access_module 模块

js
生效阶段:NGX_HTPP_ACCESS_PHASE
模块:http_access_module
默认编译进 nginx,通过 --without-http_access_module 禁用功能
生效范围:进入 access 阶段前不生效
生效阶段:NGX_HTPP_ACCESS_PHASE
模块:http_access_module
默认编译进 nginx,通过 --without-http_access_module 禁用功能
生效范围:进入 access 阶段前不生效

deny/allow:限制 IP

auth_basic:用户名密码限制

如何生成密码文件

  • 文件格式
js
# comment
name1:password1
name2:password2:comment
name3:password3
# comment
name1:password1
name2:password2:comment
name3:password3
  • 生成工具 htpasswd
js
依赖安装包:httpd-tools
htpasswd -c file -b user pass
依赖安装包:httpd-tools
htpasswd -c file -b user pass
nginx
server {
  default_type text/plain;
  
  location / {
    satisfy any;
    auth_basic "test auth_basic";
    auth_basic_user_file examples/auth.pass;
    deny all;
  }
  
  location /auth_request {
    auth_request /test_auth;
  }
  
  location = /test_auth {
    proxy_pass http://127.0.0.1:8090/auth_upstream;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}
server {
  default_type text/plain;
  
  location / {
    satisfy any;
    auth_basic "test auth_basic";
    auth_basic_user_file examples/auth.pass;
    deny all;
  }
  
  location /auth_request {
    auth_request /test_auth;
  }
  
  location = /test_auth {
    proxy_pass http://127.0.0.1:8090/auth_upstream;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}

auth_request:第三方权限控制

统一的用户权限验证系统

nginx
server {
  location / {
    auth_request /test_auth;
  }
  
  location = /test_auth {
    proxy_pass http://127.0.0.1:8090/auth_upstream;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}
server {
  location / {
    auth_request /test_auth;
  }
  
  location = /test_auth {
    proxy_pass http://127.0.0.1:8090/auth_upstream;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}

satisfy 指令

satisfy 模块允许改变模块执行顺序。

如果有 return 指令,access 阶段会生效吗?

不会生效,return 指令生效阶段在 rewrite 阶段,领先于 access。

多个 access 模块的顺序有影响吗?

  • 查看 ngx_modules.c
    • &ngx_http_auth_request_module
    • &ngx_http_auth_basic_module
    • &ngx_http_access_module

会。

输对密码,下面可以访问到文件吗?

nginx
location / {
  satisfy any;
  auth_basic "test auth_basic";
  auth_basic_user_file examples/auth.pass;
  deny all;
}
location / {
  satisfy any;
  auth_basic "test auth_basic";
  auth_basic_user_file examples/auth.pass;
  deny all;
}

可以。

如果把 deny all 提到 auth_basic 之前呢?

可以。

如果改为 allow all,有机会输入密码吗?

没有机会。

precontent 阶段

try_files 指令

默认编译进 nginx 中。

nginx
server {
  location /first {
    try_files /system/index.html $uri $uri/index.html $uri @lasturl;
  }
  
  location @lasturl {
    return 200 'lasturl!\n';
  }
  
  location /second {
    try_files $uri $uri/index.html $uri.html = 404;
  }
}
server {
  location /first {
    try_files /system/index.html $uri $uri/index.html $uri @lasturl;
  }
  
  location @lasturl {
    return 200 'lasturl!\n';
  }
  
  location /second {
    try_files $uri $uri/index.html $uri.html = 404;
  }
}

mirror 模块:实时拷贝流量

nginx
server {
  listen 10020;
  location / {
    return 200 'mirror response!';
  }
}

server {
  listen 8081;
  error_log data/error.log debug;
  
  location / {
    mirror /mirror;
    mirror_request_body off;
  }
  
  location = /mirror {
    internal; # 内部请求
    proxy_pass http://127.0.0.1:10020$request_uri;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}
server {
  listen 10020;
  location / {
    return 200 'mirror response!';
  }
}

server {
  listen 8081;
  error_log data/error.log debug;
  
  location / {
    mirror /mirror;
    mirror_request_body off;
  }
  
  location = /mirror {
    internal; # 内部请求
    proxy_pass http://127.0.0.1:10020$request_uri;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}

content 阶段

static 模块

root 和 alias 指令

都会用于把 URL 映射为文件,返回静态资源。

不过 root 会将完整 URL 映射进文件路径中,alias 只会将 location 后的 URL 路径映射到文件路径中。

root 使用范围更广,且具有默认值。

nginx
location /root {
  root html;
}

location /alias {
  alias html;
}

location ~/root/(\w+\.txt) {
  root html/first/$1;
}

location ~ /alias/(\w+\.txt) {
  alias html/first/$1;
}
location /root {
  root html;
}

location /alias {
  alias html;
}

location ~/root/(\w+\.txt) {
  root html/first/$1;
}

location ~ /alias/(\w+\.txt) {
  alias html/first/$1;
}
三个 nginx 变量

当我们访问一个 URI 文件时,会生成三个相关变量。

  • request_filename:待访问文件的完整路径
  • document_root:由 URI 和 root/alias 规则生成的文件夹路径
  • realpath_root:将 document_root 中的软链接等换成真实路径
nginx
location /RealPath/ {
  alias html/realpath/;
  return 200 '$request_filename:$document_root:$document_root:$realpath_root\n';
}
location /RealPath/ {
  alias html/realpath/;
  return 200 '$request_filename:$document_root:$document_root:$realpath_root\n';
}
静态文件返回的 content-type
未找到文件的错误日志

可以关闭文件获取不到的错误日志。

url 不以斜杠结尾访问目录

访问目录时 URL 最后没有带 / ?

static 模块实现了 root/alias 功能,发现访问目标时目录,但 URL 末尾加 / 时,会返回 301 重定向

nginx
server {
  server_name return.yueluo.club dir.yueluo.club;
  server_name_in_redirect off;
  listen 8088;
  port_in_redirect on;
  absolute_redirect off;
  
  root html/;
}
server {
  server_name return.yueluo.club dir.yueluo.club;
  server_name_in_redirect off;
  listen 8088;
  port_in_redirect on;
  absolute_redirect off;
  
  root html/;
}

index、autoindex 模块

访问 / 时的处理

显示目录内容:content 阶段的 autoindex 模块

autoindex 模块的指令

nginx
server {
  server_name autoindex.yueluo.club;
  listen 8080;
  
  location / {
    alias html/;
    autoindex on;
    # index a.html
    autoindex_exact_size off;
    autoindex_format json;
    autoindex_localtime on;
  }
}
server {
  server_name autoindex.yueluo.club;
  listen 8080;
  
  location / {
    alias html/;
    autoindex on;
    # index a.html
    autoindex_exact_size off;
    autoindex_format json;
    autoindex_localtime on;
  }
}

concat 模块:提升多个小文件性能

concat 模块的指令

nginx
server {
  server_name concat.yueluo.club;
  
  error_log logs/error.log debug;
  concat on;
  root html;
  
  location /concat {
    concat_max_files 20;
    concat_types text/plain;
    concat_unique on;
    concat_delimiter ':::'; # ::: 来分割多个文件
    concat_ignore_file_error on;
  }
}
server {
  server_name concat.yueluo.club;
  
  error_log logs/error.log debug;
  concat on;
  root html;
  
  location /concat {
    concat_max_files 20;
    concat_types text/plain;
    concat_unique on;
    concat_delimiter ':::'; # ::: 来分割多个文件
    concat_ignore_file_error on;
  }
}

log 阶段

log 模块:记录请求访问日志

功能:将 HTTP 请求相关信息记录到日志

模块:ngx_http_log_module 无法禁用

access 日志格式

配置日志文件路径

日志文件名包含变量时的优化

HTTP 过滤模块

介于 content 模块之后,log 模块之前介入请求处理。

返回响应-加工响应内容

sub 模块

替换响应中的字符串:sub 模块。ngx_http_sub_filter_module。

sub 模块的指令。

sub_fillter_last_modified:是否显示修改之前的 last_modified 内容,默认是关闭的。

sub_filter_once:只替换一次

sub_filter_types:针对什么类型进行替换,默认是 text/html。

nginx
server {
  server_name sub.yueluo.club;
  error_log logs/error.log info;
  
  location / {
    # sub_filter 'Nginx.oRg' '$host/nginx';  # 替换时忽略大小写
    # sub_filter 'nginx.cOm' '$host/nginx';
    # sub_filter_once on;
    # sub_filter_once off;
    # sub_filter_last_modifed off;
    # sub_filter_last_modifed on;
  }
}
server {
  server_name sub.yueluo.club;
  error_log logs/error.log info;
  
  location / {
    # sub_filter 'Nginx.oRg' '$host/nginx';  # 替换时忽略大小写
    # sub_filter 'nginx.cOm' '$host/nginx';
    # sub_filter_once on;
    # sub_filter_once off;
    # sub_filter_last_modifed off;
    # sub_filter_last_modifed on;
  }
}

addition 模块

响应前或者响应后增加内容。

addition 模块的指令。

nginx
server {
  server_name addition.yueluo.club;
  error_log logs/error.log info;
  
  location / {
    add_before_body /before_action;
    add_after_body /after_action;
    addition_types *;
  }
  
  location /brefore_action {
    return 200 'new content before\n';
  }
  
  location /after_action {
    return 200 'new content after\n';
  }
  
  location /testhost {
    uninitialized_variable_warn on;
    set $foo 'testhost';
    return 200 '$gzip_ratio\n';
  }
}
server {
  server_name addition.yueluo.club;
  error_log logs/error.log info;
  
  location / {
    add_before_body /before_action;
    add_after_body /after_action;
    addition_types *;
  }
  
  location /brefore_action {
    return 200 'new content before\n';
  }
  
  location /after_action {
    return 200 'new content after\n';
  }
  
  location /testhost {
    uninitialized_variable_warn on;
    set $foo 'testhost';
    return 200 '$gzip_ratio\n';
  }
}

Nginx 变量的运行原理

惰性求值

变量的特性

  • 惰性求值
  • 变量值可以时刻变化,其值为使用的那一刻的值

HTTP 框架提供的请求相关的变量

  • HTTP 请求相关的变量
  • TCP 连接相关的变量
  • Nginx 处理请求过程中产生的变量
  • 发送 HTTP 响应时相关的变量
  • Nginx 系统变量

HTTP 请求相关的变量

参数参数含义
arg_参数名URL 中某个具体参数的值
query_string与 args 变量完全相同
args全部 URL 参数
is_args如果请求 URL 中有参数则返回 ?否则返回空
content_lengthHTTP 请求中标识包体长度的 Content-Length 头部的值
content_type标识请求包体类型的 Content-Type 头部的值
uri请求的 URI (不同于 URL,不包括 ?后的参数)
document_uri与 uri 完全相同
request_uri请求的 URL(包括 URI 以及完整的参数)
scheme协议名,例如 HTTP 或者 HTTPS
request_method请求方法,例如 GET 或者 POST
request_length所有请求内容的大小,包括请求行、头部、包体等
remote_user由 HTTP Basic Authentication 协议传入的用户名
request_body_file临时存放请求包体的文件(如果包体非常小不会存文件、client_body_in_file_body 强制所有包体存入文件,且可以决定是否删除)
request_body请求中的包体,这个变量当且仅当使用反向代理,且设定用内存暂存包体时才有效
request原始的 url 请求,含有方法与协议版本,例如 GET /?a=1&b=22 HTTP/1.1
host先从请求行中获取,如果含有 Host 头部,则用其值替换掉请求行中的主机名,如果前两者都取不到,则使用匹配上的 server_name
http_头部名字返回一个具体请求头部的值(特殊:http_host、http_user_agent、http_referer、http_via、http_x_forward_for、http_cookie)

TCP 连接相关的变量

参数参数含义
binary_remote_addr客户端地址的整型格式,对于 IPv4 是 4 字节,对于 IPv6 是 16 字节
connection递增的连接序号
connection_requests当前连接上执行过的请求数,对 keepalive 连接有意义
remote_addr客户端地址
remote_port客户端端口
proxy_protocol_addr若使用 proxy_protocol 协议则返回协议中的地址,否则返回空
proxy_protocol_port若使用 proxy_protocol 协议则返回协议中的端口,否则返回空
server_addr服务端地址
server_port服务端端口
TCP_INFOtcp 内核层参数,包括 $tcpinfp_rtt,$tcpinfo_rttvar,$tcpinfo_snd_cwnd,$tcpinco_rcv_space
server_protocol服务端协议,例如 HTTP/1.1

Nginx 处理请求过程中产生的变量

参数参数含义
request_time请求处理到现在的耗时,单位为秒,精确到毫秒
server_name匹配上请求的 server_name 值
https如果开启了 TLS/SSL ,则返回 on,否则返回空
request_competion如请求处理完则返回 OK,否则返回空
request_id以 16 进制输出的请求标识 id,该 id 共含有 16 个字节,是随机生成的
request_filename待访问文件的完整路径
document_root由 URI 和 root/alias 规则生成的文件夹路径
realpath_root将 document_root 中的软链接等换成真实路径
limit_rate返回客户端响应时的速度上限,单位为每秒字节数。可以通过 set 指令修改对请求产生效果

发送 HTTP 响应时相关的变量

参数参数含义
body_bytes_sent响应中 body 包体的长度
byte_sent全部 http 响应的长度
statushttp 响应中的返回码
sent_trailer_名字把响应结尾内容里值返回
request_id以 16 进制输出的请求标识 id,该 id 共含有 16 个字节,是随机生成的
sent_http_头部名字响应中某个具体头部的值

特殊处理:

  • sent_http_content_type
  • sent_http_content_length
  • sent_http_location
  • sent_http_last_modified
  • sent_http_connection
  • sent_http_keep_alive
  • sent_http_transfer_encoding
  • sent_http_cache_control
  • sent_http_link

Nginx 系统变量

参数参数含义
time_local以本地时间标准输出的当前时间
time_iso8601使用 ISO 8601 标准输出的当前事件
nginx_versionNginx 版本号
pid所属 worker 进程的进程 id
pipe使用了管道则返回 p,否则返回 .
hostname所在服务器的主机名,与 hostname 命令输出一致
msec1970 年 1 月 1 日到现在的时间,单位为秒,小数点后精确到毫秒

referer 模块:变量防盗链

referer 模块

场景:某网站通过 url 引用了你的页面,当用户在浏览器上点击 url 时,http 请求的头部中会通过 referer 头部,将该网站当前页面的 url 带上,告诉服务器本次请求是否这个页面发起的。

目的:拒绝非正常的网站访问我们站点的资源

思路:通过 referer,用 invalid_referer 变量根据配置判断 referer 头部是否合法

referer 模块:默认编译进 Nginx,通过 --without-http_referer_module 禁用

referer 模块的指令

valid_referers 指令

可同时携带多个参数,表示多个 referer 头部都生效

secure 模块

变量值及带过期时间的配置

变量

  • secure_link
    • 值为空字符串:验证不通过
    • 值为 0:URL 过期
    • 值为 1:验证通过
  • secure_link_expires
    • 时间戳的值
nginx
server {
  location / {
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
    
    if ($secure_link = '0') {
      return 410;
    }
    
    return 200 '$secure_link:$secure_link_expires\n';
  }
}
server {
  location / {
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
    
    if ($secure_link = '0') {
      return 410;
    }
    
    return 200 '$secure_link:$secure_link_expires\n';
  }
}

仅对 URI 进行哈希

  • 将请求 URL 分为三部分,/prefix/hash/link
  • Hash 生成方式
    • 对 “link 密钥” 做 md5 哈希值
  • 用 secure_link_secret secret; 配置密钥
nginx
server {
  location /p/ {
    secure_link_secret mysecret2;
    
    if ($secure_link = " ") {
      return 403;
    }
    
    rewrite ^ /secure/$secure_link;
  }
  
  location /secure/ {
    alias html/;
    internal;
  }
}
server {
  location /p/ {
    secure_link_secret mysecret2;
    
    if ($secure_link = " ") {
      return 403;
    }
    
    rewrite ^ /secure/$secure_link;
  }
  
  location /secure/ {
    alias html/;
    internal;
  }
}

map 模块:为复杂业务生成新变量

可以根据一个或者多个变量组合而成的值做判断,修改新的变量的值,通过判断新变量的值实现复杂的业务逻辑。

nginx
map $http_host $name {
  hostnames;
    default       0;
  ~map\.tao\w+\.org.cn 1; *.taohui.org.cn   2; map.taohui.tech   3; map.taohui.*    4;
}

map $http_user_agent $mobile { 
  default       0;
  "~Opera Mini" 1; 
}

server {
  listen 8080;
  default_type text/plain;
  
  location / {
    return 200 '$name:$mobile\n';
  }
}
map $http_host $name {
  hostnames;
    default       0;
  ~map\.tao\w+\.org.cn 1; *.taohui.org.cn   2; map.taohui.tech   3; map.taohui.*    4;
}

map $http_user_agent $mobile { 
  default       0;
  "~Opera Mini" 1; 
}

server {
  listen 8080;
  default_type text/plain;
  
  location / {
    return 200 '$name:$mobile\n';
  }
}

split_client 模块:指定少量用户实现 AB 测试

nginx
split_clients "${http_testcli}" $variant {
  0.51% .one;
  20.0% .two;
  50.5% .three;
  # 40%   .four; 超过 100 不能访问
  *     "";
}

server {
  server_name split_clients.yueluo.club;
  error_log logs/error.log debug;
  default_type text/plain;;
  
  location / {
  	rteurn 200 'ABtestfile$variant\n';  
  }
}
split_clients "${http_testcli}" $variant {
  0.51% .one;
  20.0% .two;
  50.5% .three;
  # 40%   .four; 超过 100 不能访问
  *     "";
}

server {
  server_name split_clients.yueluo.club;
  error_log logs/error.log debug;
  default_type text/plain;;
  
  location / {
  	rteurn 200 'ABtestfile$variant\n';  
  }
}
js
curl -H 'testcli: 213213231adadasdad' split_clients.yueluo.club
curl -H 'testcli: 213213231adadasdad' split_clients.yueluo.club

geo 模块:根据 IP 范围的匹配生成新变量

nginx
geo $country {
  default        ZZ;
  # include        conf/geo.conf;
  proxy          116.62.160.193;
  
  127.0.0.0/24   US;
  127.0.0.1/32   RU;
  10.1.0.0/16    RU;
  192.168.1.0/24 UK; 
}

server {
  server_name geo.yueluo.club;
  
  location / {
    return 200 '$country\n';
  }
}
geo $country {
  default        ZZ;
  # include        conf/geo.conf;
  proxy          116.62.160.193;
  
  127.0.0.0/24   US;
  127.0.0.1/32   RU;
  10.1.0.0/16    RU;
  192.168.1.0/24 UK; 
}

server {
  server_name geo.yueluo.club;
  
  location / {
    return 200 '$country\n';
  }
}

geoip 模块:获取用户地理位置

基于 MaxMind 数据库从客户端获取变量

geoip_country 指令提供的变量

geoip_city 指令提供的变量

maxmind 网站,下载 c 语言代码库和地址库,对它进行编译。

nginx
geoip_country /usr/local/share/GeoIP/GeoIP.dat;
geoip_city /usr/local/share/GeoIP/GeoLiteCity.dat;
geoip_proxy 116.62.160.193/32;
geoip_proxy_recursize on;

server {
  location / {
    return 200 'country:$geoip_country_code'
  }
}
geoip_country /usr/local/share/GeoIP/GeoIP.dat;
geoip_city /usr/local/share/GeoIP/GeoLiteCity.dat;
geoip_proxy 116.62.160.193/32;
geoip_proxy_recursize on;

server {
  location / {
    return 200 'country:$geoip_country_code'
  }
}
js
curl -H 'X-Forwarded-For: ip' geoip.yueluo.club
curl -H 'X-Forwarded-For: ip' geoip.yueluo.club

对客户端使用 keeplive 提升连接效率

http 协议的 keepalive,不是指 tcp 协议的 keepalive。

四、反向代理与负载均衡

反向代理与负载均衡原理

负载均衡

nginx 在 AKF 扩展立方体上的应用

支持多种协议的反向代理

反向代理与缓存

负载均衡算法:round-robin 算法

round-robin 算法 所有负载均衡算法的基础。其他负载均衡算法失效时,降级都是 round-robin 算法。

指定上游服务器的 upstream 与 server 指令

加权 Round-Robin 负载均衡算法

Rounb-Robin:依次执行,轮询的方式

对上游服务使用 keep-alive 长连接

upstream_keepalive 指令

指定上游服务器域名解析的 resolver 指令

upserver.config

nginx
server {
  listen 8011;
  default_type text/plain;
  
  return 200 '8011 server response. \n';
}

server {
  listen 8012;
  default_type text/plain;
  # client_body_in_single_buffer on;
  
  return 200 '8012 server response. \n';
}
server {
  listen 8011;
  default_type text/plain;
  
  return 200 '8011 server response. \n';
}

server {
  listen 8012;
  default_type text/plain;
  # client_body_in_single_buffer on;
  
  return 200 '8012 server response. \n';
}

rrups.config

nginx
upstream rrups {
  server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
  server 127.0.0.1:8012;
  keepalive 32;
}

server {
  server_name rrups.yueluo.club;
  error_log /logs/error.log;
  
  location / {
    proxy_pass https://rrups;
    proxy_http_version 1;
    proxy_set_header Connection "";
  }
}
upstream rrups {
  server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
  server 127.0.0.1:8012;
  keepalive 32;
}

server {
  server_name rrups.yueluo.club;
  error_log /logs/error.log;
  
  location / {
    proxy_pass https://rrups;
    proxy_http_version 1;
    proxy_set_header Connection "";
  }
}

负载均衡算法哈希算法:ip_hash 与 hash 模块

upstream_ip_hash 模块

基于客户端 IP 地址的 Hash 算法实现负载均衡。

upstaream_hash 模块

nginx
upstream iphashups {
  ip_hash;
  # hash user_$arg_username;
  server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
  server 127.0.0.1:8012 weight=1;
}

server {
  set_real_ip_from 116.62.160.193;
  real_ip_recursize on;
  real_ip_header X-Forwared-For;
  server_name iphash.yueluo.club;
  
	error_log logs/error.log;
  access_log logs/access.log;
  
  location / {
    proxy_pass http://iphashups;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
  }
}
upstream iphashups {
  ip_hash;
  # hash user_$arg_username;
  server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
  server 127.0.0.1:8012 weight=1;
}

server {
  set_real_ip_from 116.62.160.193;
  real_ip_recursize on;
  real_ip_header X-Forwared-For;
  server_name iphash.yueluo.club;
  
	error_log logs/error.log;
  access_log logs/access.log;
  
  location / {
    proxy_pass http://iphashups;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
  }
}

一致性 Hash 算法:Hash 模块

Hash 算法的问题

宕机或者扩容时,hash 算法引发大量路由变更,导致缓存大范围失效

一致性 Hash 算法

扩容前:

扩容后:

使用一致性 Hash 算法:upstream_hash 模块

原有基础上增加配置 [consistent]

最少连接算法以及如何跨 worker 进程生效

upstream_least_conn 模块

优先选择连接最少的上游服务器。

upstream_zone 模块

使用共享内存使负载均衡对所有 worker 进程生效。

upstream 模块间的顺序:功能正常运行

upstream 模块提供的变量(不含 cache)

变量含义
upstream_addr上游服务器的 IP 地址,格式为可读的字符串,例如 127.0.0.1: 8012
upstream_connect_time与上游服务器建立连接消耗的时间,单位为秒,精确到毫秒
upstream_header_time接收上游服务发回响应中 http 头部所消耗的时间,单位为秒,精确到毫秒
upstream_response_time接收完整的上游服务响应所消耗的时间,单位为秒,精确到毫秒
upstream_http_名称从上游服务返回的响应头部的值
upstream_bytes_received从上游服务接收到的响应长度,单位为字节
upstream_response_length从上游服务返回的响应包体长度,单位为字节
upstream_status上游服务返回的 HTTP 响应中的状态码。如果未连接上,该变量值为 502
upstream_cookie_名称从上游服务器发回的响应头 Set-Cookie 中取出的 cookie 值
upstream_trailer_名称从上游服务的响应尾部取到的值

proxy 模块处理请求的流程

proxy 模块中的 proxy_pass 指令

HTTP 协议的反向代理:proxy 模块

nginx
server {
  listen 8011;
  default_type text/plain;
  
  return 200 '8011 server response. \n';
}

server {
  listen 8012;
  default_type text/plain;
  
  return 200 '8012 server response. uri:$uri \n';
}
server {
  listen 8011;
  default_type text/plain;
  
  return 200 '8011 server response. \n';
}

server {
  listen 8012;
  default_type text/plain;
  
  return 200 '8012 server response. uri:$uri \n';
}
nginx
upstream proxyups {
  server 127.0.0.1:8012 weight=1;
}

server {
  server_name proxy.yueluo.club;
  error_log logs/error.log;
  access_log logs/upstream_access.log varups;
  
  location /a {
    proxy_pass http://proxyups/addurl;
  }
}
upstream proxyups {
  server 127.0.0.1:8012 weight=1;
}

server {
  server_name proxy.yueluo.club;
  error_log logs/error.log;
  access_log logs/upstream_access.log varups;
  
  location /a {
    proxy_pass http://proxyups/addurl;
  }
}
js
proxy_pass http://proxyups/addurl;

curl proxy.yueluo.club/a/b/c => uri:/a/b/c
proxy_pass http://proxyups/addurl;

curl proxy.yueluo.club/a/b/c => uri:/a/b/c
js
proxy_pass http://proxyups/addurl/www;

curl proxy.yueluo.club/a/b/c => uri:/www/b/c
proxy_pass http://proxyups/addurl/www;

curl proxy.yueluo.club/a/b/c => uri:/www/b/c

proxy_pass 是动作类指令,指定某个阶段使用 proxy 模块处理请求,proxy_pass 是否加 url 可能产生不同的结果。

根据指令修改发往上游的请求

proxy 模块:生成发往上游的请求行

proxy 模块:生成发往上游的请求头部

proxy 模块:生成发往上游的包体

nginx
server {
  listen 8011;
  default_type text/plain;
  
  return 200 '8011 server response. \n';
}

server {
  listen 8012;
  default_type text/plain;
  
  return 200 '8012 server response. $uri $request_method $request $http_name \n';
}
server {
  listen 8011;
  default_type text/plain;
  
  return 200 '8011 server response. \n';
}

server {
  listen 8012;
  default_type text/plain;
  
  return 200 '8012 server response. $uri $request_method $request $http_name \n';
}
nginx
upstream proxyups {
  server 127.0.0.1:8012 weight=1;
}

server {
  server_name proxy.yueluo.club;
  error_log logs/error.log;
  access_log logs/upstream_access.log varups;
  
  location /a {
    proxy_pass http://proxyups/addurl;
    proxy_method POST;
    proxy_pass_request_headers off;
    proxy_pass_request_body off;
    proxy_set_body 'hello-wrold';
    proxy_set_header name '';
    proxy_http_version 1.1;
    proxy_set_header Connection "";
  }
}
upstream proxyups {
  server 127.0.0.1:8012 weight=1;
}

server {
  server_name proxy.yueluo.club;
  error_log logs/error.log;
  access_log logs/upstream_access.log varups;
  
  location /a {
    proxy_pass http://proxyups/addurl;
    proxy_method POST;
    proxy_pass_request_headers off;
    proxy_pass_request_body off;
    proxy_set_body 'hello-wrold';
    proxy_set_header name '';
    proxy_http_version 1.1;
    proxy_set_header Connection "";
  }
}

接收用户请求包体的方式

接收客户端请求的包体:收完再转发还是边收变转发

客户端包体的接收

最大包体长度限制

临时文件路径格式

读取包体的超时

与上游服务器建立连接

与上游服务器建立连接

当出现状态码 502 时,可以换一个服务器继续处理,通过 proxy_next_upstream。

上游连接启动 TCP keepalive

上游连接启用 HTTP keepalive

修改 TCP 连接中的 local address

当客户端关闭连接时

接收上游的响应

接收上游的 HTTP 响应头部

限定了上游 server 最大值。

接收上游的 HTTP 包体

及时转发包体

接收上游时网络速度相关指令

上游包体的持久化

nginx
server {
  listen 8012;
  default_type text/plain;
  
  root html;
  
  location / {
    
  }
  
  location /test {
    return 200 '8012 server resonse.';
  }
}
server {
  listen 8012;
  default_type text/plain;
  
  root html;
  
  location / {
    
  }
  
  location /test {
    return 200 '8012 server resonse.';
  }
}
nginx
upstream proxyups {
  server 127.0.0.1:8012 weight=1;

server {
  listen 80;
  server_name store.yueluo.club;
  
  error_log logs/error.log debug;
  
  root /tmp;
  
  location / {
    proxy_pass http://proxyups;
    proxy_store on;
    proxy_store_access user:rw group:rw all:r;
  }
}
upstream proxyups {
  server 127.0.0.1:8012 weight=1;

server {
  listen 80;
  server_name store.yueluo.club;
  
  error_log logs/error.log debug;
  
  root /tmp;
  
  location / {
    proxy_pass http://proxyups;
    proxy_store on;
    proxy_store_access user:rw group:rw all:r;
  }
}
js
cur store.yueluo.club tech/a.txt
cur store.yueluo.club tech/a.txt

处理上游的响应头部

五、Nginx 系统层性能优化

六、深入使用 Nginx 与 OpenResty