目录


文章目录

  • 目录
  • 主流的网络 I/O 框架
  • C10K 问题
  • 高性能网络 I/O 服务器模型
  • 阻塞 IO + 多线程
  • Reactor 模型(事件驱动处理模型)
  • 单 Reactor,单线程模型
  • 单 Reactor,多线程模型
  • 主从 Reactor,多线程模型
  • Proactor 模型
  • 主流的中间件所采用的网络 I/O 模型


主流的网络 I/O 框架

  • netty
  • gnet
  • libevent
  • evio (golang)
  • ACE (c++)
  • boost::asio (c++)
  • muduo (linux only)

C10K 问题

字母 C,表示:Concurrent(并发),C10K,即:10000 高并发。

虽然现在通过现成的网络 I/O 框架,例如:libevent、Netty 等,可以轻松地完成这个目标。但是在二十多年前,突破这个并发量还是非常困难的。

高并发网络 I/O 主要需要注意以下几点:

  1. Linux 的文件句柄数量​:每个 Socket 连接的代表是一个文件描述符,如果 Linux 的文件句柄数量(默认:1024)不够用,那么新的连接将会被丢弃并产生错误。
  2. Linux 的系统内存​:每个 Socket 连接都需要占用发送缓冲区(e.g. tcp_rmem 文件)和接收缓冲区(e.g. tcp_wmem 文件)地址空间。假设一个连接需要 128K 空间,1w 个连接就需要 1.2G 的 Userspace 地址空间。
  3. 网络带宽​:假设 10 个连接,每个连接跑 10Mpps,包长为 64Byte,那么带宽为:10*(10Mpps​1024**2) * (64Byte​8) / (1024**3) = 50Gbps。

NOTE​:BW = pps(包个数)* bit/p(包大小)

高性能网络 I/O 服务器模型

阻塞 IO + 多线程

由此,基于阻塞 IO,常见的 I/O 模型是:阻塞 IO + 多线程。例如:每个 TCP Connect 都使用一个单独的线程进来行处理。

但是,Application 能够创建的线程数量是有限的,因为线程本身就是一种开销。所以,就导致了 Application 可以处理的 Clients 数量受限制。面对百万连接的情况,是无法处理。

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_线程模型

Reactor 模型(事件驱动处理模型)

所谓 Reactor(反应堆),即:在一个单独的线程中运行的、用于负责监听和分发事件的逻辑。比分:我们的手机可以设置为转接,将来自前任的电话转接给适当的联系人。

如下图,Application Server 通过统一的 ServiceHandler(Reactor)来进行 Request Dispatch(请求分发)。所以,这种软件架构也称为事件驱动处理模型(Event Loop)。

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_线程组_02

可见,Reactor(Event Loop)模型的本质是 “I/O 多路复用 + 线程池” 实现的网络 I/O 模型:

  1. 将 I/O 操作抽象为事件,为每个事件设置回调函数。
  2. 将非 I/O 操作解耦到其他 Worker 线程中,释放 Reactor Dispatch 的压力。
  3. Application 通过 Reactor Dispatch(实现了 I/O 多路复用)来统一监听事件,并在收到事件之后进行任务分发。

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_客户端_03

根据 Reactor 的数量和处理的资源大小通常又分为:

  • 单 Reactor,单线程模型。
  • 单 Reactor,多线程模型。
  • 主从 Reactor,多线程模型。

单 Reactor,单线程模型

该模型,通常是只有一个 epoll 对象,所有的接收客户端连接、客户端读取、客户端写入操作都包含在一个线程内。

需要注意的是,在模式中,不仅 I/O 操作在该 Reactor 线程上,连非 I/O 的业务操作也在该线程上进行处理了,这可能会大大延迟 I/O 请求的响应。所以我们应该将非 I/O 的业务逻辑操作从 Reactor 线程上卸载,以此来加速 Reactor 线程对 I/O 请求的响应。

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_线程组_04

单 Reactor,多线程模型

该模型中,将读写的业务逻辑交给具体的线程池来实现,以此提升系统性能。

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_客户端_05

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_线程模型_06

在线程池模式中,虽然非 I/O 操作交给了线程池来处理,但是所有的 I/O 操作依然由 Reactor 单线程执行,在高负载、高并发或大数据量的应用场景,依然较容易成为瓶颈。所以,对于 Reactor 的优化,又产生出下面的多线程模式。

主从 Reactor,多线程模型

在这种模型中,主要分为两个部分:mainReactor、subReactors。

  • mainReactor:负责接收客户端的连接,然后将建立的客户端连接通过负载均衡的方式分发给 subReactors。
  • subReactors:负责具体的每个连接的读写,对于非 IO 的操作,依然交给工作线程池去做,对逻辑进行解耦。

mainReactor 对应 Netty 中配置的 BossGroup 线程组,主要负责接受客户端连接的建立。一般只暴露一个服务端口,BossGroup 线程组一般一个线程工作即可 subReactor 对应 Netty 中配置的 WorkerGroup 线程组,BossGroup 线程组接受并建立完客户端的连接后,将网络 socket 转交给 WorkerGroup 线程组,然后在 WorkerGroup 线程组内选择一个线程,进行 I/O 的处理。WorkerGroup 线程组主要处理 I/O,一般设置 2*CPU 核数个线程。

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_客户端_07

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_线程模型_08

Proactor 模型

proactor 主要是通过对异步 IO 的封装的一种模型,它需要底层操作系统的支持,目前只有 windows 的 IOCP 支持的比较好。

主流的中间件所采用的网络 I/O 模型

Linux Kernel TCP/IP Stack — L7 Layer — 高性能网络 I/O 服务器模型_线程组_09