单点服务器的 7 种并发模型

简介: 模型一:单线程 Accept(无 IO 复用)模型二:单线程 Accept + 多线程读写业务(无 IO 复用)模型三:单线程多路 IO 复用模型四:单线程多路 IO 复用 + 多线程业务工作池模型五:单线程多路 IO 复用 + 多线程多路 IO 复用(线程池)模型五(进程版)单进程多路 IO 复用 + 多进程多路 IO 复用(进程池)模型六:单线程多路 IO 复用 + 多线程多路 IO 复用 + 多线程
学习视频: 深入理解 Linux 中网络I/O复用并发模型_哔哩哔哩
欢迎关注刘舟冰老师的 B 站主页: 刘丹冰Aceld

Socket Server 并发模型

前置知识:

  • socket 网络编程
  • 多路 IO 复用机制
  • 多线程 / 多进程等并发编程理论

模型一:单线程 Accept(无 IO 复用)

(1)模型结构图

(2)模型分析

① 主线程 main thread 执行阻塞 Accept,每次客户端 Connect 连接过来,main thread 中 Accept 响应并建立连接。

② 创建连接成功,得到 Connfd1 套接字后, 依然在 main thread 串行处理套接字读写,并处理业务。

③ 在 ② 处理业务中,如果有新客户端 Connect 过来,Server 无响应,直到当前套接字全部业务处理完毕。

④ 当前客户端处理完后,完毕连接,处理下一个客户端请求。

(3)优缺点

优点:

  • socket 编程流程清晰且简单,适合学习使用,了解 socket 基本编程流程。

缺点:

  • 该模型并非并发模型,是串行的服务器,同一时刻,监听并响应最大的网络请求量为 1,即并发量为 1
  • 仅适合学习基本 socket 编程,不适合任何服务器 Server 构建。

模型二:单线程 Accept + 多线程读写业务(无 IO 复用)

(1)模型结构图

(2)模型分析

① 主线程 main thread 执行阻塞 Accept,每次客户端 Connect 连接过来,main thread 中 Accept 响应并建立连接。

② 创建连接成功,得到 Connfd1 套接字后,创建一个新线程 thread1 用来处理客户端的读写业务。main thread 依然回到 Accept 阻塞等待新客户端。

thread1 通过套接字 Connfd1 与客户端进行通信读写。

④ Server 在 ② 处理业务中,如果有新客户端 Connect 过来,main threadAccept 依然响应并建立连接,重复 ②。

(3)优缺点

优点:

  • 基于 模型一:单线程 Accept(无IO复用) 支持了并发的特性。
  • 使用灵活,一个客户端对应一个线程单独处理,Server 处理业务内聚程度高,客户端无论如何写,服务端均会有一个线程做资源响应。

缺点:

  • 随着客户端的数量增多,需要开辟的线程也增加,客户端与 Server 线程数量呈 1:1 关系。因此对于高并发场景,线程数量受到硬件上限瓶颈。线程过多也会增加 CPU 的切换成本,降低 CPU 的利用率。
  • 对于长连接,客户端一旦无业务读写,只要不关闭,Server 的对应线程依然需要保持连接(心跳、健康监测等机制),占用连接资源和线程开销资源浪费。
  • 仅适合客户端数量不大,并且数量可控的场景使用。
  • 仅适合学习基本 socket 编程,不适合作为服务器 Server 构建。

模型三:单线程多路 IO 复用

(1)模型结构图

(2)模型分析

① 主线程 main thread 创建 listenFd 之后,采用多路 I/O 复用机制(如 select、epoll)进行 IO 状态阻塞监控。有 Client1 客户端 Connect 请求,I/O 复用机制检测到 ListenFd 触发读事件,则进行 Accept 建立连接,并将新生成的 connFd1 加入到 监听I/O集合 中。

Client1 再次进行正常读写业务请求,main thread多路I/O复用机制阻塞返回,会触该套接字的 读 / 写 事件等。

③ 对于 Client1 的读写业务,Server 依然在 main thread 执行流程提继续执行,此时如果有新的客户端 Connect 连接请求过来,Server 将没有即时响应。

④ 等到 Server 处理完一个连接的 Read + Write 操作,继续回到多路I/O复用机制阻塞,其他连接过来重复 ②、③ 流程。

(3)优缺点

优点:

  • 单流程解决了可以同时监听多个客户端读写状态的模型,不需要 1:1 与客户端的线程数量关系。
  • 多路 I/O 复用阻塞,非忙询状态,不浪费 CPU 资源, CPU 利用率较高。

缺点:

  • 虽然可以监听多个客户端的读写状态,但是同一时间内,只能处理一个客户端的读写操作,实际上读写的业务并发为 1
  • 多客户端访问 Server,业务为串行执行,大量请求会有排队延迟现象,如图中 ⑤ 所示,当 Client3 占据 main thread 流程时,Client1, Client2 流程卡在 IO复用 等待下次监听触发事件。

模型四:单线程多路 IO 复用 + 多线程业务工作池

(1)模型结构图

(2)模型分析

① 主线程 main thread 创建 listenFd 之后,采用多路 I/O 复用机制(如 select、epoll)进行 IO 状态阻塞监控。有 Client1 客户端 Connect 请求,I/O 复用机制检测到 ListenFd 触发读事件,则进行 Accept 建立连接,并将新生成的 connFd1 加入到 监听I/O集合 中。

② 当 connFd1 有可读消息,触发读事件,并且进行读写消息。

main thread 按照固定的协议读取消息,并且交给 Worker Pool 工作线程池, 工作线程池在 Server 启动之前就已经开启固定数量的 thread,里面的线程只处理消息业务,不进行套接字读写操作。

④ 工作池处理完业务,触发 connFd1 写事件,将回执客户端的消息通过 main thead 写给对方。

该模型虽然提高了处理事件业务的能力,但是由于入口和出口都是 main thread,效率和 模型三 一样。

(3)优缺点

优点:

  • 对于 模型三, 将业务处理部分,通过工作池分离出来,减少多客户端访问 Server,业务为串行执行,大量请求会有排队延迟时间。
  • 实际上读写的业务并发为 1,但是业务流程并发为 Worker Pool 线程数量,加快了业务处理并行效率。

缺点:

  • 读写依然为 main thread 单独处理,最高读写并行通道依然为 1
  • 虽然多个 worker 线程处理业务,但是最后返回给客户端,依旧需要排队,因为出口还是 main threadRead + Write

模型五:单线程多路 IO 复用 + 多线程多路 IO 复用(线程池)

(1)模型结构图

(2)模型分析

① Server 在启动监听之前,开辟固定数量 N 的线程,用 Thead Pool 线程池管理。

② 主线程 main thread 创建 listenFd 之后,采用多路 I/O 复用机制(如 select、epoll)进行 IO 状态阻塞监控。有 Client1 客户端 Connect 请求,I/O 复用机制检测到 ListenFd 触发读事件,则进行 Accept 建立连接,并将新生成的 connFd1 分发给 Thread Pool 中的某个线程进行监听。

Thread Pool 中的每个 thread 都启动多路 I/O 复用机制,用来监听 main thread 建立成功并且分发下来的 socket 套接字。

④ 如图, thread1 监听 ConnFd1、ConnFd2thread2 监听 ConnFd3thread3 监听 ConnFd4。 当对应的 ConnFd 有读写事件,对应的线程处理该套接字的读写及业务。

(3)优缺点

优点:

  • main thread 的单流程读写,分散到多线程完成,这样增加了同一时刻的读写并行通道,并行通道数量 NN 为线程池 Thread 数量。
  • Server 同时监听的 ConnFd 套接字 数量几乎成倍增大,之前的全部监控数量取决于 main thread多路I/O复用机制的最大限制(select 默认为 1024, epoll 默认与内存大小相关,约 3~6w 不等),所以理论单点 Server 最高响应并发数量为N * (3~6W)N 为线程池 Thread 数量,建议与 CPU 核心成比例 1:1)。
  • 如果良好的线程池数量和 CPU 核心数适配,那么可以尝试 CPU 核心与 Thread 进行绑定,从而降低 CPU 的切换频率,提升每个 Thread 处理合理业务的效率,降低 CPU 切换成本开销。

缺点:

  • 虽然监听的并发数量提升,但是最高读写并行通道依然为 N,而且多个身处同一个 Thread 的客户端,会出现读写延迟现象,实际上每个 Thread 的模型特征与 模型三:单线程多路IO复用 一致。

模型五(进程版)单进程多路 IO 复用 + 多进程多路 IO 复用(进程池)

Nginx 采用的就是这种模型的改进版。

(1)模型结构图

(2)模型分析

五、单线程IO复用+多线程IO复用(线程池) 无大差异。

不同处:

  • 进程和线程的内存布局不同导致,main process (主进程)不再进行 Accept 操作,而是将 Accept 过程分散到各个 子进程(process) 中。
  • 进程的特性,资源独立,所以 main process 如果 Accept 成功的 fd,其他进程无法共享资源,所以需要各子进程自行 Accept 创建链接
  • main process 只是监听 ListenFd 状态,一旦触发读事件(有新连接请求)。 通过一些 IPC(进程间通信:如信号、共享内存、管道)等,让各自子进程 Process 竞争 Accept 完成连接建立,并各自监听。

(3)优缺点

五、单线程IO复用+多线程IO复用(连接线程池) 无大差异。

不同处:

  • 多进程内存资源空间占用稍微大一些
  • 多进程模型安全稳定型较强,这也是因为各自进程互不干扰的特点导致。

模型六:单线程多路 IO 复用 + 多线程多路 IO 复用 + 多线程

(1)模型结构图

(2)模型分析

① Server 在启动监听之前,开辟固定数量 N 的线程,用 Thead Pool 线程池管理。

② 主线程 main thread 创建 listenFd 之后,采用多路 I/O 复用机制(如 select、epoll)进行 IO 状态阻塞监控。有 Client1 客户端 Connect 请求,I/O 复用机制检测到 ListenFd 触发读事件,则进行 Accept 建立连接,并将新生成的 connFd1 分发给 Thread Pool 中的某个线程进行监听。

Thread Pool 中的每个 thread 都启动多路I/O复用机制,用来监听 main thread 建立成功并且分发下来的 socket 套接字。一旦其中某个被监听的客户端套接字触发 I/O读写事件,那么,会立刻开辟一个新线程来处理 I/O读写 业务。

④ 当某个读写线程完成当前读写业务,如果当前套接字没有被关闭,那么将当前客户端套接字(如:ConnFd3)重新加回线程池的监控线程中,同时自身线程自我销毁。

(3)优缺点

优点

  • 模型五、单线程IO复用+多线程IO复用(连接线程池) 基础上,除了能够保证同时响应的 最高并发数,又能解决 读写并行通道 局限的问题。
  • 同一时刻的读写并行通道,达到 最大化极限,一个客户端可以对应一个单独执行流程处理读写业务,读写并行通道与客户端数量 1:1 关系。

缺点

  • 该模型过于理想化,因为要求 CPU 核心数量足够大。
  • 如果硬件 CPU 数量可数(目前的硬件情况),那么该模型将造成大量的 CPU 切换成本浪费。因为为了保证读写并行通道与客户端 1:1 的关系,那么 Server 需要开辟的 Thread 数量就与客户端一致,那么线程池中做 多路I/O复用 的监听线程池绑定 CPU 数量将变得毫无意义。
  • 如果每个临时的读写 Thread 都能够绑定一个单独的CPU,那么此模型将是最优模型。但是目前 CPU 的数量无法与客户端的数量达到一个量级,目前甚至差的不是几个量级的事。

总结

以上 7 种服务器处理模型,各自有其特点和优势。对于应付高并发和高 CPU 利用率的模型,目前多数是模型五(或模型五进程版的,如 Nginx 就是模型五进程版的改进版)。

至于并发模型并非设计的约复杂越好,也不是线程开辟的越多越好,同时需要考虑硬件的利用与和切换成本的开销。模型六设计就极为复杂,线程较多,但是如今的硬件能力无法支撑,反倒导致该模型性能极差。所以对于不同的业务场景也要选择适合的模型,并不是一定固定就要使用某个。

相关文章
|
8月前
|
消息中间件 存储 Kafka
谈一谈Kafka在高性能和数据一致性之间做的妥协与改进
CAP定理是分布式系统的基本定理,描述了一致性、可用性和分区容错性三大特性,只能满足两种,开发者必须在此做出取舍。而 Kafka 作为一款高性能的消息队列与分布式存储系统,必然要在高性能和数据一致性之间做出取舍,本文在这方面做了一番探索。
|
10月前
|
XML JSON 监控
高并发设计系列-分布式篇
高并发设计系列-分布式篇
|
11月前
|
消息中间件 开发框架 NoSQL
【工作中问题解决实践 二】分布式消息并发同步处理方案
【工作中问题解决实践 二】分布式消息并发同步处理方案
86 0
|
消息中间件 负载均衡 JavaScript
浅谈分布式系统中的补偿机制设计问题
浅谈分布式系统中的补偿机制设计问题
|
存储 缓存 Kubernetes
实战并发-使用分布式缓存和有限状态机
实战并发-使用分布式缓存和有限状态机
实战并发-使用分布式缓存和有限状态机
使用lockf()保证应用单进程
使用lockf()保证应用单进程
84 0
|
新零售 消息中间件 存储
保证分布式系统数据一致性的6种方案
在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性?
4124 0
|
缓存 网络协议 算法
怎样提高服务器并发处理能力?
什么是服务器并发处理能力? 一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强。 服务器的本质工作就是,争取以最快的速度将内核缓冲区中的用户请求数据一个不剩地都拿出来,然后尽快处理,再将响应数据放到一块又能够与发送数据的缓冲区中,接着处理下一拨请求。 有什么方法衡量服务器并发处理能力? 一,吞吐率 量化指标:吞吐率,单位时间里服务器处理的最大请求数,单位req/s。 再深入一些,HTTP请求通常是对不同资源的请求,也就是请求不同的URL,有的是请求图片,有的是获取动态内容,有的是静态页面,显然这些请求所花费的时间各不相同,而这些请求再不同时间
291 0
|
缓存
怎么理解分布式、高并发、多线程?
是不是很多人都认为分布式=高并发=多线程?
910 0
|
缓存 Java 调度
分布式、高并发、多线程,到底有什么区别?
当提起这三个词的时候,是不是很多人都认为分布式=高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼? 确实,在一开始接触的时候,不少人都会将三者混淆,误以为所谓的分布式高并发的系统就是能同时供海量用户访问,而采用多线程手段不就是可以提供系统的并发能力吗?实际上,他们三个总是相伴而生,但侧重点又有不同。
2237 0