Skip to content

netpoll

Go 的 netpoll 是 Go 语言运行时(runtime)实现的核心 I/O 多路复用机制,它使得 Go 能够以简洁的同步编程模型(每个连接一个 Goroutine)实现高性能的网络服务。其本质是将操作系统底层的 I/O 多路复用系统调用(如 Linux 的 epoll)与 Go 的 Goroutine 调度器深度结合。

Netpoll与原生GoNet对比

  1. Goroutine数目 Go Net:一个Goroutine下只有一个连接。

NetPoll:一个Goroutine下有多个连接。

  1. goroutine 上下文切换调度 压力 Go Net: 高并发时 切换压力大。 因为 GoNet 一个链接对应一个goroutine,goroutine数目多时切换频繁。

NetPoll:高并发时 切换压力 小。 因为 NetPoll 多个链接共用一个goroutine,切换次数少。

  1. 内存消耗: Go Net: 内存消耗‌与连接数正相关‌,高并发时可能因Goroutine数量爆炸导致内存压力‌

NetPoll:预先分配缓冲区,创建缓冲区池。

  1. 触发方式 Go Net:使用 边缘触发 ET。一次读完数据,实现简单。

NetPoll:使用 水平触发 LT。不需要预分配足够大的内存做数据缓冲区。

  1. 是否支持内核和用户层共用buff池,少拷贝一次数据。 Go Net:不支持。

NetPoll:支持。管理一个Buffer池直接交给用户,少拷贝一次。

  1. 适用场景 Go Net:低并发简单场景

NetPoll:高并发低延迟场景

netpoll 设计思路

  1. 基本架构 原生网络库 基于epoll lt模式 开发,基本架构如下图所示: netpoll 基本架构

  2. goroutine的使用 存在一个 poll对象池,每个poll对象带有一个epoll,和单独对应一个 goroutine。 goroutine数目和 poll对象数目一致,每个poll对象可以监听多个 文件描述符fd

  3. IO读写逻辑

    • 每个poll对象开启一个goroutine来不断轮询当前epoll上的可读可写等事件
    • 每个epoll会关联多个fd,每个fd关联一个 Buffer。
    • 当从 fd 监听到有可读事件后,会把数据读到 Buffer里。
    • 不断轮询处理 Buffer 的数据,当发现数据读完整之后,通知后面的处理逻辑的协程池 GoPoll去执行
    • 与内核的系统调用交互完全由netpoll控制,用户层对 Conn 的读写都只是在操作共用的Buffer而已。
  4. 优劣势 优势: 支持高并发能力更强。

    • (1) 轮询处理Buffer的数据,不存在数据得不到处理的情况。
    • (2) 一个goroutine对应多个连接,即使连接非常多,资源调度切换上的开销也不大。
    • (3) 支持用户和内核共享buff,减少一次数据拷贝操作,提升效率。

劣势:需要占用更多的内存。

Golang学习网由www.golangdev.cn整理维护