nginx之待续-没写完

nginx从大学到现在一直都在用,前前后后也写过很多了,这里来一篇尽可能全面的(大言不惭)

🧱 一、Nginx ------一个"超级前台小哥"

像咱们程序员我也不代表所有了,大部分程序员朋友对于未来的设想基本上都是开个店,那么如果你开了一家超火爆的火锅店(你的网站/应用),每天成千上万食客(用户请求)蜂拥而至。

  • 如果没有 Nginx:每个食客都直接冲进后厨(你的应用服务器,比如 Node.js、Python Flask、Java Tomcat),对着厨师大喊:"我要毛肚、宽粉!快点上!"
    → 厨师崩溃了,锅都烧穿了,还被投诉上菜慢。
  • 有了 Nginx:门口站了个冷静、高效、记忆力超群的前台小哥 (Nginx)。
    • 食客来了,先找他。
    • 他会快速判断:"哦,你要的是菜单(静态文件)?我这儿就有,给你!"(直接返回 HTML/CSS/JS/图片)
    • 如果你要的是现炒菜(动态内容),他就说:"稍等,我帮你叫后厨。"然后把请求转给后厨(反向代理)。
    • 如果后厨忙不过来,他还知道有多个后厨(负载均衡),自动分配任务,绝不让一个累死。
    • 有人想捣乱(DDoS攻击)?他一眼识破,直接轰出去(限流、安全防护)。

SO, Nginx是咱们高性能的"请求调度员 + 静态资源快递员"

why

大多数服务器(常用的店Apache)用的是 "多进程/多线程"模型 :(单进程/单线程现在哪有市场,根本吃不开呀!)

→ 来一个请求,就fork 一个新进程或线程去处理。(redis还有谁是不是也有fork,知识的思想都是相通的)

→ 10000 个请求?那就开 10000 个线程!结果:内存爆炸、上下文切换累死 CPU,像开了 10000 个微信窗口同时聊天。

这也太不智能了,这可不用,卡卡的

Nginx 用的是 事件驱动 + 异步非阻塞 I/O 模型(基于 epoll/kqueue 等系统调用)

想象前台小哥不是一个人干所有事,而是一个主控大脑 + 一堆小纸条。

  • 主线程master process咱们的大脑:只管招人、发工资(只读配置,管理 worker)

  • 真正干活的是几个 worker 进程 (通常 = CPU 核心数);真正处理网络 I/O(每个独立进程,避免锁竞争,没在线程切换上浪费)CPU 利用率高

    nginx.conf

    worker_processes auto; # 通常设为 CPU 核心数(auto = 自动检测)
    worker_rlimit_nofile 65535; # 每个 worker 能打开的最大文件描述符(FD)

  • 每个worker 进程内部 :重点point!

    • 不为每个请求开新线程!

    • 用一个事件循环(event loop),盯着一堆"待办事项"(文件描述符 FD)

      events {
      use epoll; # 强制使用 epoll(Linux 高效 I/O 多路复用)
      worker_connections 10240; # 每个 worker 最大并发连接数
      multi_accept on; # 一次 accept() 尽可能接收多个连接 减少系统调用
      }
      比如:
      // Nginx 内部简化逻辑:accept()、read()都是非阻塞的 → 不会卡住 event loop
      epoll_fd = epoll_create(1);
      listen_sock = socket(); bind(); listen();
      set_nonblocking(listen_sock); // 关键:设为非阻塞!
      //只通知"有事件的 FD",不像 select/poll 遍历全部
      epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, EPOLLIN);

      while (1) {
      int n = epoll_wait(epoll_fd, events, max_events, timeout);
      for (i=0; i<n; i++) {
      if (events[i].data.fd == listen_sock) {
      while (conn = accept4(listen_sock, ..., SOCK_NONBLOCK)) {
      // 接收新连接(非阻塞!一次收完所有 pending 连接)
      epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn, EPOLLIN|EPOLLET);
      }
      } else {
      // 处理已连接 socket 的读写(非阻塞 read/write)
      handle_io(events[i].data.fd);
      }
      }
      }

    • 当某个请求的数据从网卡到了(可读事件),它就处理一下;处理完要发回去,就注册"可写事件",然后立刻去处理下一个请求。
    • 全程不等!不睡!不卡! 就像闪电侠在多个任务间瞬移。
    • Linux 进程间内存隔离,一个worker 崩了不影响其他,避免全局解释器锁(GIL)类问题。性能更稳定

咱们Nginx 利用 Linux 的 epoll(或 BSD 的 kqueue)实现高效的 I/O 多路复用。它把 socket 设置为 非阻塞模式,配合事件通知机制,避免了传统"阻塞 read() 卡住整个线程"的问题。

📦 三、模块化设计 ------ "乐高式"组装功能:可嵌套、可继承

Nginx 本身很小,但通过模块可以变身全能战士:灵活得像乐高

  • 核心模块:进程管理、配置解析、事件驱动(地基)

Nginx 配置文件(nginx.conf)不是命令,而是声明"当遇到什么情况,就做什么":

复制代码
server {
    listen 80;
    server_name example.com;

    location /static/ {//像if-else规则,匹配 URL 路径
        root /data;          # 静态文件,直接返回
    }
    //每个请求按阶段处理:rewrite → access → content → log ...
    location /api/ {
        proxy_pass http://backend;  # 动态请求,转发给后端
    }
}
修改配置后 nginx -s reload,平滑重启:
master 启动新 worker,老 worker 处理完手头请求自动退出 ------ 用户无感知!
  • HTTP 模块:处理 Web 请求(主力部队)

  • Mail 模块:还能当邮件代理(隐藏技能)

  • 第三方模块:Lua 脚本(OpenResty)、gRPC 代理、WAF 防火墙......

    //原生Nginx 不支持 Lua,但 OpenResty = Nginx + LuaJIT + 模块,广泛用于 API 网关
    //Lua 代码在 Nginx 的 access 阶段执行
    location /private/ {
    access_by_lua_block {
    local token = ngx.req.get_headers()["Authorization"]
    if not token or token ~= "Bearer secret123" then
    ngx.exit(401)//API 直接操作请求/响应
    end
    }
    proxy_pass http://app;
    }

    如果不想装 OpenResty,也可用 nginx-lua-module 编译进 Nginx。

比如你想加个"用户登录验证"?写个 Lua 脚本挂到 access_by_lua 阶段就行 ------ 像给前台小哥戴个智能眼镜,实时识别 VIP。

模块在编译时静态链接 or 运行时动态加载(1.9.11+),灵活又高效。

🔄 四、反向代理 & 负载均衡 ------ "甩锅大师"

Nginx 最常用的功能之一:反向代理(Reverse Proxy)。

  • 用户以为在和 Nginx 说话,其实 Nginx 转头就去找后端服务器(Tomcat/Node.js 等)。

  • 对外隐藏了真实服务器 IP,安全又方便。

    upstream backend {
    server 10.0.0.10:8080 weight=3 max_fails=2 fail_timeout=30s;
    server 10.0.0.11:8080 weight=1;
    server 10.0.0.12:8080 backup; # 备用节点

    复制代码
      keepalive 32;  # 每个worker维护最多32个空闲后端连接:避免频繁建连,与后端建独立TCP连接

    }
    //Nginx 先收完客户端请求体(或流式转发),再发给后端
    server {
    listen 80;
    location / {
    proxy_pass http://backend; # 注意:这里用 upstream 名

    复制代码
          # 透传客户端信息 后端看到的来源IP是 Nginx 的内网 IP。
          proxy_set_header Host  $ host;
          #通过 X-Real-IP 传递真实用户 IP
          proxy_set_header X-Real-IP  $ remote_addr;
          proxy_set_header X-Forwarded-For  $ proxy_add_x_forwarded_for;
    
          # 超时设置(防后端卡死)
          proxy_connect_timeout 5s;
          proxy_send_timeout 10s;
          proxy_read_timeout 10s;
      }

    }

负载均衡算法(把请求分给多个后端):

  • round-robin(默认):轮流来,公平!
  • least_conn:谁连接少给谁,照顾弱鸡服务器
  • ip_hash:同一个 IP 永远打给同一台,解决 session 问题
  • weight:给性能强的服务器多分点活("壮汉多搬砖"原则)

Nginx 维护一个后端服务器列表,根据算法选一个,然后用 upstream 模块建立新连接转发数据。

健康检查(max_fails, fail_timeout),自动踢掉宕机的后端 ------ 像个无情的 HR。

📁 五、静态文件服务 ------ "本地快递员"

Nginx 发静态文件快到飞起?拷贝 sendfile

复制代码
location /static/ {
    root /var/www;
    sendfile on;              # 启用零拷贝
    tcp_nopush on;            # 等待包满再发(减少小包)
    tcp_nodelay off;          # 与 nopush 配合,优化吞吐
    open_file_cache max=1000 inactive=60s;  # 缓存文件元数据
}
  • 直接调用 sendfile() 系统调用(零拷贝技术)!

    • 传统方式:磁盘 → 内核缓冲区 → 用户缓冲区 → socket 缓冲区 → 网卡(3次拷贝)
    • sendfile:磁盘 → 内核缓冲区 → 网卡(1次拷贝,CPU 几乎不参与)
  • 支持 gzip 压缩、ETag 缓存、expires 过期头,减少带宽和重复请求。

    磁盘 → 内核页缓存 ────[DMA]───→ 网卡
    ↑ 只传指针,无 CPU 拷贝

strace -e trace=sendfile,read,write -p $ (pgrep nginx | head -1)

访问 /static/test.jpg

sendfile(10, 9, NULL, 123456) = 123456用了零拷贝,如看到 read+write配置没生效

🔒 六、安全与限流 ------ "保安队长"

  • 限流(Rate Limiting) :
    limit_req 模块基于漏桶算法,防止恶意刷接口。
    → "每人每秒只能点一次单,插队?滚!"

  • 访问控制 :
    allow/deny IP,auth_basic 密码保护。

  • 防 CC 攻击 :
    结合 limit_conn 限制单 IP 并发连接数。

  • TLS/SSL 终止 :
    HTTPS 解密在 Nginx 做,后端用 HTTP,减轻应用负担。

    http {
    // binary_remote_addr:用二进制 IP 作 key(比字符串省内存) //zone=perip:10m:分配 10MB 共享内存存状态(所有 worker 共享) //rate 漏桶(Leaky Bucket),水滴入速度 = rate limit_req_zone binary_remote_addr zone=perip:10m rate=10r/s;

    复制代码
      server {
          location /api/ {
              limit_req zone=perip burst=20 nodelay;
              # burst=20:桶容量:允许突发 20 个请求 漏桶(Leaky Bucket)
              # nodelay:突发时不延迟,直接处理(否则排队)
          }
      }

    }

    测试:用 ab -n 100 -c 10 http://yoursite/api/,观察是否限流

💡 总结:Nginx 的底层哲学:用不同维度思考网络 I/O

特性 传统服务器(如 Apache) Nginx
并发模型 多线程/多进程(C10K 问题) 事件驱动 + 异步非阻塞(轻松 C100K+)
资源消耗 高内存、高上下文切换 低内存、CPU 高效
架构 单体,功能耦合 模块化,按需组装
角色 全能但笨重 专注 I/O 调度,做"最靓的仔"
要素 配置指令 内核机制
高并发 worker_processes, worker_connections epoll/kqueue + 非阻塞 I/O
低延迟 tcp_nodelay, keepalive Nagle 算法控制 + 连接复用
高吞吐 sendfile, tcp_nopush 零拷贝 + 聚合发送
高可用 upstream, max_fails 健康检查 + 故障转移

Nginx 真言:做人就要敢说敢做!

"我不生产内容,我只是请求的搬运工;

但我搬运得比谁都快、都稳、都省电。"

相关推荐
RobinDevNotes14 分钟前
从 Electron 到 GPUI,9 个跨平台桌面方案该怎么选
前端·javascript·rust·electron·openresty·前端开发工具
jay神1 小时前
基于 YOLOv8 的轴承缺陷检测(Web 版)
前端·人工智能·yolo
jay神1 小时前
基于 YOLOv8 的森林火灾检测系统(Web 版)
前端·yolo
code2cat1 小时前
HTML基础篇之第一张网页:从index.html到浏览器里的任务清单
前端·html
miss2 小时前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan3 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真3 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment3 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan3 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么3 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模