Posts 再看nginx网络架构
Post
Cancel

再看nginx网络架构

  1. Nginx 把所有 socket(包括 listen socket)都统一抽象成 ngx_connection_t。
  2. Nginx 把所有 epoll/kqueue 事件都统一抽象成 ngx_event_t。
  3. Nginx 把所有协议都统一抽象成 ngx_protocol_t。
  4. Nginx 把所有模块都统一抽象成 ngx_module_t。

第一章:Nginx 网络模型与协议架构核心设计

Nginx 的核心并不是 HTTP 服务器,而是一个基于事件驱动的通用网络框架。HTTP、TCP、UDP 等协议都只是构建在其网络抽象之上的“插件”。

理解 nginx 源码的关键,不在于协议细节,而在于它的 网络层三大基础对象模型


一、Nginx 网络模型的本质

nginx 的设计非常克制,它没有像传统服务器那样为每种协议构建独立网络栈,而是统一抽象为:

事件驱动 + 连接驱动 + 状态机驱动

所有网络行为都围绕 epoll/kqueue 事件展开:

  • 连接建立
  • 数据读写
  • 超时处理
  • 关闭连接

这些全部由事件系统驱动。


二、Nginx 的三大网络基础对象(核心章节)

这是理解 nginx 源码的“第一性原理”。

2.1 三个核心对象

nginx 的网络层实际上只有三个基础对象:

对象作用所在层
ngx_listening_t监听 socket(listen 端口)core
ngx_connection_tTCP 连接抽象core
ngx_event_tepoll/kqueue 事件event

2.2 三者关系模型

1
2
3
4
5
6
7
8
9
10
            Nginx 网络核心
                    │
        ┌───────────┼───────────┐
        │           │           │
 ngx_listening_t  ngx_event_t  ngx_connection_t
   (监听socket)      (事件)        (TCP连接)
        │                           │
        └───────────┬───────────────┘
                    ▼
              连接生命周期

2.3 三个对象的职责

(1)ngx_listening_t —— 监听 socket

它表示一个 listen 端口,例如:

1
2
3
listen 80;
listen 443;
listen 3306;

本质是对系统 listen socket 的封装。

关键字段:

  • fd:监听 socket
  • sockaddr:地址信息
  • handler:accept 后的入口函数
  • backlog / buffer 配置

👉 最关键字段:

handler(协议分发入口)

它决定 accept 后进入哪个模块:

  • HTTP → ngx_http_init_connection
  • STREAM → ngx_stream_init_connection
  • MAIL → ngx_mail_init_connection

(2)ngx_connection_t —— TCP连接抽象

这是 nginx 最核心的数据结构。

所有连接都是它:

  • client → nginx
  • nginx → upstream
  • listen socket 本身

关键字段:

  • fd:连接描述符
  • read/write event
  • data:协议对象挂载点

👉 关键设计点:

ngx_connection_t 不绑定协议

协议是后面挂的。


(3)ngx_event_t —— IO事件

表示 epoll/kqueue 的事件:

  • EPOLLIN(可读)
  • EPOLLOUT(可写)
  • error
  • timeout

核心字段:

  • handler:事件回调函数
  • active:是否注册

nginx 的 IO 本质是:

event → handler → 状态机推进


2.4 一个核心总结

nginx 网络层只有三个对象:

ngx_listening_t + ngx_connection_t + ngx_event_t

所有 HTTP / TCP / UDP 逻辑都建立在这三者之上。


三、协议层对象(HTTP / stream / mail)

在网络层之上,nginx 才引入协议层。

协议层的本质:

挂在 ngx_connection_t 上的状态机


3.1 协议对象列表

模块协议对象
httpngx_http_request_t
streamngx_stream_session_t
mailngx_mail_session_t

3.2 协议对象挂载方式

核心机制:

1
2
3
4
ngx_connection_t
      
      
   void *data

不同协议挂不同对象:

HTTP

1
c->data = ngx_http_request_t

STREAM

1
c->data = ngx_stream_session_t

MAIL

1
c->data = ngx_mail_session_t

3.3 本质理解

ngx_connection_t = 通用 TCP 连接 协议对象 = 连接上的状态机


四、完整连接生命周期

一个 TCP 请求在 nginx 中的完整路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
客户端连接
      │
      ▼
ngx_listening_t(监听socket)
      │
      ▼
epoll 触发
      │
      ▼
ngx_event_accept()
      │
      ▼
accept() 获取 fd
      │
      ▼
ngx_connection_t 创建
      │
      ▼
ls->handler(c)
      │
      ├── HTTP → ngx_http_init_connection
      │            └── ngx_http_request_t
      │
      ├── STREAM → ngx_stream_init_connection
      │              └── ngx_stream_session_t
      │
      └── MAIL → ngx_mail_init_connection
                   └── ngx_mail_session_t

五、为什么 Nginx 可以支持多协议

因为它做了三层解耦:

5.1 网络层统一

所有 IO 统一:

  • ngx_connection_t
  • ngx_event_t

5.2 协议层插件化

协议模块只需要实现:

  • 初始化连接
  • 状态机处理
  • 事件回调

5.3 协议与网络完全分离

core 不关心:

  • HTTP
  • TCP proxy
  • TLS

core 只负责:

epoll + connection + event


六、stream 模块的本质

以 stream 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
client TCP
   │
   ▼
ngx_connection_t
   │
   ▼
ngx_stream_session_t
   │
   ▼
proxy_pass
   │
   ▼
upstream connection

stream 的特点:

  • 不解析 HTTP
  • 不关心应用层协议
  • 只处理字节流

七、Nginx 架构的本质总结

nginx 实际是一个:

事件驱动的通用网络框架,而不是 HTTP Server

它的核心模型:

1
event → connection → protocol state machine

八、本章核心总结

请记住这一张图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
          Nginx 网络模型
────────────────────────────

ngx_listening_t   → 监听socket
        │
        ▼
ngx_event_t       → epoll事件
        │
        ▼
ngx_connection_t  → TCP连接
        │
        ▼
protocol object   → HTTP / STREAM / MAIL
        │
        ▼
state machine     → 请求/会话处理

第二章:ngx_event_accept 函数

p51 p52 p53 p54

核心就一句话:lc 是门,c 是进来的客人。


lc 是 nginx 启动时就创建好的”监听连接”,它的 fd 是 bind()+listen() 之后的监听 socket。这个 socket 不能读数据,只能调 accept()。epoll 检测到它”可读”的意思不是”有数据来了”,而是”有新的 TCP 连接在排队等着被接受”。整个 worker 进程跑起来,lc 就一直在那,永远不会关闭。

c 是每次 accept4(lc->fd, ...) 后产生的,内核从等待队列里取出一个已完成三次握手的连接,返回一个全新的 fd。nginx 再从预分配的连接池里取一个 ngx_connection_t 对象绑上这个新 fd,这才是 c。后续所有读写数据、HTTP 解析、WAF 检测,都在 c 上进行。请求结束后 c 放回池子,但 lc 全程没动过。

所以 lc = ev->data 这行的意思是:epoll 触发了,ev 就是 lc 的读事件,ev->data 指回 lc 本身,再从 lc->listening 拿到配置 ls。这三行就是”确认是哪个端口的事件触发了,拿到对应的配置”。之后 accept4() 一调,lc 的使命在这次循环里就结束了,接下来全是 c 的事。

This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags