一、背景 / 为什么学Nginx是一个高性能的Web服务器和反向代理服务器。国内流行有两个开源版本淘宝Tengine项目和由张亦春发起的开源项目OpenResty项目。特点内存占用少相比Apache等传统服务器资源消耗极低。并发能力强采用事件驱动的异步架构轻松处理上万并发连接。用途广泛做静态文件服务、反向代理、负载均衡、静态前置、Lua编程、限流。低资源、高稳定、高并发。二、怎么工作的普通 Nginx 怎么工作的Worker 进程Nginx 启动后会创建多个Worker进程通常等于你的CPU核数Nginx Master老板只负责管理 ├── Worker 1干活的工人 ├── Worker 2干活的工人 ├── Worker 3干活的工人 └── Worker 4干活的工人每一个Worker 独立处理请求互不干扰。每个Worker内部如何处理请求时间驱动非阻塞I/O方式处理请求✅ 非阻塞模式Nginx 的方式 服务员走到 A 桌问您想点什么 A 桌说我再看看菜单... 服务员立刻说好的您想好了叫我然后转身去 B 桌 B 桌也还没想好又转身去 C 桌 谁先举手事件触发就先服务谁这就是事件驱动的本质不等任何一个请求谁准备好就处理谁。OpenResty 把Lua塞进了NginxOpenResty 在 Nginx 的每个 Worker 里嵌入了一个 Lua 虚拟机LuaJIT VM。OpenResty 的 Worker ┌──────────────────────────┐ │ Nginx Worker │ │ ┌────────────────────┐ │ │ │ 处理 HTTP 请求 │ │ │ │ 解析 header │ │ │ │ 转发 proxy_pass │ │ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │ │ Lua 虚拟机 │ │ │ ← 多了这个 │ │ │ (可以执行代码) │ │ │ │ │ └──────────────┘ │ │ │ └────────────────────┘ │ └──────────────────────────┘实现在Nginx里编程——比如在请求到达后端之前做鉴权、限流、改写等操作。一个 Lua VM 里怎么同时处理上万个请求一个Worker 里面只有一个Lua虚拟机如何处理成千上万的请求。并且每个请求互不干扰。答案Lua协程Coroutine一个 Worker 进程 │ └── 一个 Lua 虚拟机 │ ├── 协程 A处理 用户1 的请求 ├── 协程 B处理 用户2 的请求 ├── 协程 C处理 用户3 的请求 ├── ... └── 协程 N处理 用户N 的请求那么协程是什么✅ 协程模式 只有你 1 个厨师但有 10 口锅 你在这口锅里翻炒几下盖上盖子让它焖着 立刻转身去另一口锅翻炒几下再盖上盖子 回来第一口锅焖好了继续炒 如此反复10 道菜同时进行注意协程之间是数据隔离的但共享同一个Lua VMNginx Master 进程管理员 │ ┌─────────┬───────────┼───────────┬──────────┐ │ │ │ │ │ Worker 1 Worker 2 Worker 3 Worker 4 ... │ │ │ │ ┌────┴────┐ │ │ │ │Lua VM 1 │ │ │ │ ← 每个Worker有自己的Lua VM ├─────────┤ │ │ │ │协程 A │ │ │ │ │协程 B │ │ │ │ │协程 C │ │ │ │ │... │ │ │ │ └─────────┘ │ │ │ 每个协程处理一个请求 协程挂起时不占CPU 事件如Redis返回触发时恢复执行总结OpenResty Nginx事件驱动处理连接 Lua 协程事件驱动处理业务逻辑双层异步所以单机就能抗住超高并发。三、底层架构多进程模型最核心的架构Nginx 启动 │ ├── Master 进程1个老板 │ ├── 读取配置文件 │ ├── 管理 Worker 的启动和停止 │ └── 接收外部信号reload、stop 等 │ ├── Worker 进程几个 CPU几核工人 │ ├── Worker 1 ←→ CPU 核心 0 │ ├── Worker 2 ←→ CPU 核心 1 │ ├── Worker 3 ←→ CPU 核心 2 │ └── Worker 4 ←→ CPU 核心 3 │ └── 三种通信机制 ├── 信号kill命令 → Master 监听外部控制 ├── 频道socketpair → Master 给 Worker 下达指令 └── 共享内存shm → Worker 之间共享数据常用信号命令生产每天都在用nginx -s reload #热加载 nginx -s stop #快速停止 nginx -s quit #处理完请求再停止 nginx -s reopen #重新打开日志日志切割使用惊群问题与进程调度面试高频什么是惊群场景1 万个连接同时进来 无调度模式 4 个 Worker 同时被惊醒都去抢这个连接 只有 1 个能抢到其他 3 个白忙活 → 瞬间浪费大量 CPU 资源 就像门铃响了4 个人同时跑去开门只有 1 个人能开到Nginx 的三种解决方案方式原理效果现状五调度所有Worked 争抢资源有惊群但高并发下影响不大1.11.3 之后默认就是这个互斥锁抢到锁的Worker才能接受新连接消除惊群但长连接场景负载不均默认关闭套接字分片reseport内核之间分配给某个Worker无惊群。性能最好生产推荐开启生产建议 server { listen 80 reuseport; # 加上 reuseport让内核来调度性能最优 server_name example.com; }时间驱动 epoll (性能核心)epoll 是什么epoll 是 Linux 内核提供的高效事件通知器。epoll 方式 Nginx 说“我注册了这 1 万个连接有数据来了通知我” 内核说“好的” → 只有真正有数据到达的连接才会通知 Nginx → 不用一个一个检查效率极高。工作机制请求的生命周期每一HTTP请求在Nginx中内部需要经过11个阶段就像流水线一样。请求到达 Nginx │ ▼ ┌─────────────────────────────────────────────┐ │ ① 收件登记读请求头 [自动] │ │ ② 改地址 v1server级rewrite [你配置] │ │ ③ 找柜台匹配location [自动] │ │ ④ 改地址 v2location级rewrite [你配置] │ │ ⑤ 防死循环检查 [自动] │ ├─────────────────────────────────────────────┤ │ ⑥ 查限流保安你来太频繁了 [你配置] │ ← 被拦→直接返回503 │ ⑦ 查权限保安你没工牌 [你配置] │ ← 被拦→直接返回403 │ ⑧ 裁决 [自动] │ ├─────────────────────────────────────────────┤ │ ⑨ 最后准备try_files等 [你配置] │ │ ⑩ 核心处理读文件/转发后端 [你配置] │ ← 最关键的阶段 │ ⑪ 写日志 [你配置] │ └─────────────────────────────────────────────┘ │ ▼ 响应返回给用户模块化架构Nginx本质上是一个模块加载器几乎所有的功能是模块实现的。核心模块crore进程管理 事件模块event:eloll HTTP模块http请求处理全流程 stream模块TCP/UTP代理比如mysql、redis Mail 模块邮件代理 第三方模块Lua扩展、监控、WAF小结四、配置文件结构与语法配置文件的位置与结构# 主配置文件通常在这里 /etc/nginx/nginx.conf # 结构是层级嵌套的像俄罗斯套娃 # 全局块影响整体 ├── events 块连接处理相关 └── http 块所有 HTTP 相关 ├── server 块一个虚拟主机 │ ├── location 块URL 匹配规则 │ └── location 块 └── server 块 └── location 块反向代理用户不知道后端是谁server { listen 80; server_name www.example.com; location /api/ { proxy_pass http://127.0.0.1:8080/; # 转发到后端 proxy_set_header Host $host; # 传递原始域名 proxy_set_header X-Real-IP $remote_addr; # 传递真实IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { root /usr/share/nginx/html; # 静态文件自己处理 index index.html; } }负载均衡http { # 定义上游服务器组 upstream backend_servers { # 策略一轮询默认 server 192.168.1.10:8080; server 192.168.1.11:8080; server 192.168.1.12:8080; # 策略二加权轮询性能强的多干活 # server 192.168.1.10:8080 weight3; # server 192.168.1.11:8080 weight1; # 策略三ip_hash同一用户打到同一后端解决session会话问题 # ip_hash; # 策略四least_conn谁闲着给谁 # least_conn; } server { listen 80; location / { proxy_pass http://backend_servers; } } }Location 匹配规则location /exact {} #1.精确匹配完全等于 location ^~ /prefix {} #2.前缀匹配不检查正则 location ~ \.php$ {} #3.正则匹配区分大小写按照文件出现的先后顺序第一个命中为结果。 location ~* \.(jpg|png) #4. 正则匹配不区分大小写。限制 location /genral #普通匹配 locatino / #兜底记忆口诀精确 前缀^~ 正则 普通前缀 默认
【07-Nginx】
一、背景 / 为什么学Nginx是一个高性能的Web服务器和反向代理服务器。国内流行有两个开源版本淘宝Tengine项目和由张亦春发起的开源项目OpenResty项目。特点内存占用少相比Apache等传统服务器资源消耗极低。并发能力强采用事件驱动的异步架构轻松处理上万并发连接。用途广泛做静态文件服务、反向代理、负载均衡、静态前置、Lua编程、限流。低资源、高稳定、高并发。二、怎么工作的普通 Nginx 怎么工作的Worker 进程Nginx 启动后会创建多个Worker进程通常等于你的CPU核数Nginx Master老板只负责管理 ├── Worker 1干活的工人 ├── Worker 2干活的工人 ├── Worker 3干活的工人 └── Worker 4干活的工人每一个Worker 独立处理请求互不干扰。每个Worker内部如何处理请求时间驱动非阻塞I/O方式处理请求✅ 非阻塞模式Nginx 的方式 服务员走到 A 桌问您想点什么 A 桌说我再看看菜单... 服务员立刻说好的您想好了叫我然后转身去 B 桌 B 桌也还没想好又转身去 C 桌 谁先举手事件触发就先服务谁这就是事件驱动的本质不等任何一个请求谁准备好就处理谁。OpenResty 把Lua塞进了NginxOpenResty 在 Nginx 的每个 Worker 里嵌入了一个 Lua 虚拟机LuaJIT VM。OpenResty 的 Worker ┌──────────────────────────┐ │ Nginx Worker │ │ ┌────────────────────┐ │ │ │ 处理 HTTP 请求 │ │ │ │ 解析 header │ │ │ │ 转发 proxy_pass │ │ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │ │ Lua 虚拟机 │ │ │ ← 多了这个 │ │ │ (可以执行代码) │ │ │ │ │ └──────────────┘ │ │ │ └────────────────────┘ │ └──────────────────────────┘实现在Nginx里编程——比如在请求到达后端之前做鉴权、限流、改写等操作。一个 Lua VM 里怎么同时处理上万个请求一个Worker 里面只有一个Lua虚拟机如何处理成千上万的请求。并且每个请求互不干扰。答案Lua协程Coroutine一个 Worker 进程 │ └── 一个 Lua 虚拟机 │ ├── 协程 A处理 用户1 的请求 ├── 协程 B处理 用户2 的请求 ├── 协程 C处理 用户3 的请求 ├── ... └── 协程 N处理 用户N 的请求那么协程是什么✅ 协程模式 只有你 1 个厨师但有 10 口锅 你在这口锅里翻炒几下盖上盖子让它焖着 立刻转身去另一口锅翻炒几下再盖上盖子 回来第一口锅焖好了继续炒 如此反复10 道菜同时进行注意协程之间是数据隔离的但共享同一个Lua VMNginx Master 进程管理员 │ ┌─────────┬───────────┼───────────┬──────────┐ │ │ │ │ │ Worker 1 Worker 2 Worker 3 Worker 4 ... │ │ │ │ ┌────┴────┐ │ │ │ │Lua VM 1 │ │ │ │ ← 每个Worker有自己的Lua VM ├─────────┤ │ │ │ │协程 A │ │ │ │ │协程 B │ │ │ │ │协程 C │ │ │ │ │... │ │ │ │ └─────────┘ │ │ │ 每个协程处理一个请求 协程挂起时不占CPU 事件如Redis返回触发时恢复执行总结OpenResty Nginx事件驱动处理连接 Lua 协程事件驱动处理业务逻辑双层异步所以单机就能抗住超高并发。三、底层架构多进程模型最核心的架构Nginx 启动 │ ├── Master 进程1个老板 │ ├── 读取配置文件 │ ├── 管理 Worker 的启动和停止 │ └── 接收外部信号reload、stop 等 │ ├── Worker 进程几个 CPU几核工人 │ ├── Worker 1 ←→ CPU 核心 0 │ ├── Worker 2 ←→ CPU 核心 1 │ ├── Worker 3 ←→ CPU 核心 2 │ └── Worker 4 ←→ CPU 核心 3 │ └── 三种通信机制 ├── 信号kill命令 → Master 监听外部控制 ├── 频道socketpair → Master 给 Worker 下达指令 └── 共享内存shm → Worker 之间共享数据常用信号命令生产每天都在用nginx -s reload #热加载 nginx -s stop #快速停止 nginx -s quit #处理完请求再停止 nginx -s reopen #重新打开日志日志切割使用惊群问题与进程调度面试高频什么是惊群场景1 万个连接同时进来 无调度模式 4 个 Worker 同时被惊醒都去抢这个连接 只有 1 个能抢到其他 3 个白忙活 → 瞬间浪费大量 CPU 资源 就像门铃响了4 个人同时跑去开门只有 1 个人能开到Nginx 的三种解决方案方式原理效果现状五调度所有Worked 争抢资源有惊群但高并发下影响不大1.11.3 之后默认就是这个互斥锁抢到锁的Worker才能接受新连接消除惊群但长连接场景负载不均默认关闭套接字分片reseport内核之间分配给某个Worker无惊群。性能最好生产推荐开启生产建议 server { listen 80 reuseport; # 加上 reuseport让内核来调度性能最优 server_name example.com; }时间驱动 epoll (性能核心)epoll 是什么epoll 是 Linux 内核提供的高效事件通知器。epoll 方式 Nginx 说“我注册了这 1 万个连接有数据来了通知我” 内核说“好的” → 只有真正有数据到达的连接才会通知 Nginx → 不用一个一个检查效率极高。工作机制请求的生命周期每一HTTP请求在Nginx中内部需要经过11个阶段就像流水线一样。请求到达 Nginx │ ▼ ┌─────────────────────────────────────────────┐ │ ① 收件登记读请求头 [自动] │ │ ② 改地址 v1server级rewrite [你配置] │ │ ③ 找柜台匹配location [自动] │ │ ④ 改地址 v2location级rewrite [你配置] │ │ ⑤ 防死循环检查 [自动] │ ├─────────────────────────────────────────────┤ │ ⑥ 查限流保安你来太频繁了 [你配置] │ ← 被拦→直接返回503 │ ⑦ 查权限保安你没工牌 [你配置] │ ← 被拦→直接返回403 │ ⑧ 裁决 [自动] │ ├─────────────────────────────────────────────┤ │ ⑨ 最后准备try_files等 [你配置] │ │ ⑩ 核心处理读文件/转发后端 [你配置] │ ← 最关键的阶段 │ ⑪ 写日志 [你配置] │ └─────────────────────────────────────────────┘ │ ▼ 响应返回给用户模块化架构Nginx本质上是一个模块加载器几乎所有的功能是模块实现的。核心模块crore进程管理 事件模块event:eloll HTTP模块http请求处理全流程 stream模块TCP/UTP代理比如mysql、redis Mail 模块邮件代理 第三方模块Lua扩展、监控、WAF小结四、配置文件结构与语法配置文件的位置与结构# 主配置文件通常在这里 /etc/nginx/nginx.conf # 结构是层级嵌套的像俄罗斯套娃 # 全局块影响整体 ├── events 块连接处理相关 └── http 块所有 HTTP 相关 ├── server 块一个虚拟主机 │ ├── location 块URL 匹配规则 │ └── location 块 └── server 块 └── location 块反向代理用户不知道后端是谁server { listen 80; server_name www.example.com; location /api/ { proxy_pass http://127.0.0.1:8080/; # 转发到后端 proxy_set_header Host $host; # 传递原始域名 proxy_set_header X-Real-IP $remote_addr; # 传递真实IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { root /usr/share/nginx/html; # 静态文件自己处理 index index.html; } }负载均衡http { # 定义上游服务器组 upstream backend_servers { # 策略一轮询默认 server 192.168.1.10:8080; server 192.168.1.11:8080; server 192.168.1.12:8080; # 策略二加权轮询性能强的多干活 # server 192.168.1.10:8080 weight3; # server 192.168.1.11:8080 weight1; # 策略三ip_hash同一用户打到同一后端解决session会话问题 # ip_hash; # 策略四least_conn谁闲着给谁 # least_conn; } server { listen 80; location / { proxy_pass http://backend_servers; } } }Location 匹配规则location /exact {} #1.精确匹配完全等于 location ^~ /prefix {} #2.前缀匹配不检查正则 location ~ \.php$ {} #3.正则匹配区分大小写按照文件出现的先后顺序第一个命中为结果。 location ~* \.(jpg|png) #4. 正则匹配不区分大小写。限制 location /genral #普通匹配 locatino / #兜底记忆口诀精确 前缀^~ 正则 普通前缀 默认