字节开源的netpoll中,一个poller对应一个Sub-Reactor
目录
在字节跳动开源的高性能网络库 netpoll 中,一个 poller 实际上就对应一个 Sub-Reactor,这是典型的 Reactor 模式(多Reactor) 中的实现方式。
✅ 理解关系:poller 与 Sub-Reactor
在 netpoll 中:
-
Poller:
- 是事件循环(event loop)的具体实现,用于监听一组文件描述符(fd)上的 IO 事件。
- 负责调用底层的
epoll(Linux)或kqueue(BSD/macOS)来检测事件。 - 一般会配合一个 goroutine 持续运行,形成一个独立的事件处理线程。
-
Sub-Reactor:
- 是指专门处理连接上的 IO 事件(如读写),不负责接受新连接(这是 Main Reactor 的职责)。
- 每个 Sub-Reactor 监听一批连接,接收到事件后分发给处理逻辑。
因此在 netpoll 中:
一个
poller就是一个 Sub-Reactor,它专注于处理已建立连接上的读写事件。
🎯 为什么这么设计?
- 高性能: 每个 poller 独立处理一批连接,可以充分利用多核 CPU。
- 避免锁竞争: 每个 poller 管自己的连接组,减少了共享状态带来的锁开销。
- 可扩展: 支持根据 CPU 核心数启多个 poller(即多个 Sub-Reactor),提高吞吐。
🔧 一个简单的类比:
假设你有 8 核 CPU:
runtime.NumCPU()返回 8。- netpoll 会默认启用 8 个 poller(除非你自己配置)。
- 每个 poller(Sub-Reactor)跑在一个 goroutine 中,监听一部分连接。
- 如果你用
listener接收连接,可以通过负载均衡把连接注册到不同 poller 上。
✅ 总结:
| 术语 | netpoll 中的角色 | 功能 |
|---|---|---|
| Poller | Sub-Reactor | 处理连接的 IO(读写)事件 |
| Main Reactor | listener |
接收新连接,并分配给 Poller |
| Event Loop | poller 内部循环 | 持续调用 epoll/kqueue 等 |
通过图示 + 简化源码结构的方式,来说明字节跳动 netpoll 中 poller 与 Sub-Reactor 的实现逻辑,包括其初始化和事件循环处理方式。
🔧 netpoll 架构简图:Main Reactor + Sub-Reactors (Pollers)
┌───────────────────────┐
│ Main Reactor │
│ (Acceptor) │
└─────────┬─────────────┘
│
Accept new connections
▼
┌──────────────────────────┐
│ Sub-Reactor / Poller 1 │
│ [Event Loop in Goroutine]│
└──────────────────────────┘
│
▼
Handle IO Events
┌──────────────────────────┐
│ Sub-Reactor / Poller 2 │
│ [Event Loop in Goroutine]│
└──────────────────────────┘
│
▼
Handle IO Events
...
- Main Reactor(主反应器):监听新连接,通过
netpoll.Listener接收连接。 - Sub-Reactor(子反应器)= Poller:通过
epoll_wait等机制监听注册的连接读写事件。 - 每个 poller 是一个 goroutine,独立运行 event loop。
⚙️ Poller 初始化与事件循环简化版代码
下面是根据 netpoll 源码的关键结构进行简化描述,便于理解:
1. 初始化 Poller 管理器(EventLoopSet)
// 创建 PollerManager(管理多个 poller)
pollerManager := netpoll.NewEventLoopSet(func(conn netpoll.Connection) error {
// 这是处理 IO 事件的回调
return handle(conn)
})
// 启动所有 poller(通常 = NumCPU)
pollerManager.Run()
2. 创建 Listener(Main Reactor)
listener, err := netpoll.CreateListener("tcp", "127.0.0.1:8080")
// 接受连接并注册到 poller 中
go func() {
for {
conn, err := listener.Accept()
poller := pollerManager.Next() // 负载均衡地取一个 poller
poller.AddRead(conn) // 注册连接到该 poller 的 epoll
}
}()
3. 每个 Poller 的事件循环(内部)
func (p *poller) run() {
for {
events := p.waitEvents() // epoll_wait / kqueue
for _, ev := range events {
conn := ev.conn
if ev.readable {
handle(conn) // 调用上层回调处理数据
}
}
}
}
📌 核心点总结
| 模块 | 说明 |
|---|---|
poller |
一个 poller 就是一个 Sub-Reactor,内部跑一个 goroutine,不断监听 epoll 事件 |
EventLoopSet |
管理多个 poller,封装了多核并发 |
AddRead |
注册连接到具体 poller 的 epoll/kqueue |
Listener.Accept() |
主反应器只负责接受连接,不做 IO |
✅ 更深入理解建议
-
查看源代码文件:
poller.go:Poller 的结构与 event loop 实现eventloop.go:EventLoopSet 管理多个 pollerlistener.go:创建主反应器并接收连接
-
示例项目推荐:
examples/echo/:netpoll 自带的 echo server 使用了完整流程
以下是使用 Mermaid 绘制的 netpoll 网络模型中 Main Reactor 与多个 Sub-Reactor (Poller) 的关系图:
✅ Mermaid 图(Main Reactor -> Pollers)

✅ Mermaid 图(事件循环内逻辑)

🎯 描述要点:
Main Reactor只负责接收连接。- 每个连接根据策略分配到某个
poller(即 Sub-Reactor)。 - 每个 poller 独立监听和处理 IO 事件,形成 N 个事件循环。
- 每个事件循环用
epoll_wait或kqueue等底层机制实现。
如你在开发中使用 netpoll,有更多细节(如连接状态、协程池、buffer 管理)也可以继续深入探讨。是否需要我也给出一个完整的 echo server 使用示例?
---
### 🎯 描述要点:
* `Main Reactor` 只负责接收连接。
* 每个连接根据策略分配到某个 `poller`(即 Sub-Reactor)。
* 每个 poller 独立监听和处理 IO 事件,形成 N 个事件循环。
* 每个事件循环用 `epoll_wait` 或 `kqueue` 等底层机制实现。
如你在开发中使用 netpoll,有更多细节(如连接状态、协程池、buffer 管理)也可以继续深入探讨。是否需要我也给出一个完整的 echo server 使用示例?
</div>
</font>
Do not communicate by sharing memory; instead, share memory by communicating.

浙公网安备 33010602011771号