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 真言:做人就要敢说敢做!

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

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

相关推荐
GISer_Jing2 小时前
大语言模型Agent入门指南
前端·数据库·人工智能
运筹vivo@2 小时前
BUUCTF: [极客大挑战 2019]Upload
前端·web安全·php·ctf
qq_12498707532 小时前
基于Spring Boot的长春美食推荐管理系统的设计与实现(源码+论文+部署+安装)
java·前端·spring boot·后端·毕业设计·美食·计算机毕业设计
运筹vivo@2 小时前
攻防世界: easyupload
前端·web安全·php·ctf
UI设计兰亭妙微3 小时前
兰亭妙微:以HTML前端、UI/交互/图标设计赋能数字孪生与大屏设计新标杆
前端·ui·用户体验设计
jarreyer3 小时前
【AI编程】claudecode插件配置记录和trae软件相关配置
前端·chrome·ai编程
奔跑的web.3 小时前
TypeScript 类型断言
前端·javascript·typescript
ヤ鬧鬧o.3 小时前
HTML多倒计时管理
前端·javascript·css·html5
知兀3 小时前
【uniapp/vue3+ts/js】eslint9+prettier+husky+lint-staged
前端·javascript·uni-app