原创声明 作者: 刘丹冰 Aceld,微信公众号同名
本文主要介绍常见的 Server 的并发模型,这些模型与编程语言本身无关,有的编程语言可能在语法上直接透明了模型本质,所以开发者没必要一定要基于模型去编写,只是需要知道和了解并发模型的构成和特点即可。
那么在了解并发模型之前,我们需要两个必备的前置知识:
① 主线程main thread
执行阻塞 Accept,每次客户端 Connect 链接过来,main thread
中 accept 响应并建立连接
② 创建链接成功,得到Connfd1
套接字后, 依然在main thread
串行处理套接字读写,并处理业务。
③ 在②处理业务中,如果有新客户端Connect
过来,Server
无响应,直到当前套接字全部业务处理完毕。
④ 当前客户端处理完后,完毕链接,处理下一个客户端请求。
优点:
缺点:
该模型并非并发模型,是串行的服务器,同一时刻,监听并响应最大的网络请求量为1
。 即并发量为1
。
仅适合学习基本 socket 编程,不适合任何服务器 Server 构建。
① 主线程main thread
执行阻塞 Accept,每次客户端 Connect 链接过来,main thread
中 accept 响应并建立连接
② 创建链接成功,得到Connfd1
套接字后,创建一个新线程thread1
用来处理客户端的读写业务。main thead
依然回到Accept
阻塞等待新客户端。
③ thread1
通过套接字Connfd1
与客户端进行通信读写。
④ server 在②处理业务中,如果有新客户端Connect
过来,main thread
中Accept
依然响应并建立连接,重复②过程。
优点:
模型一:单线程 Accept (无 IO 复用)
支持了并发的特性。server
处理业务内聚程度高,客户端无论如何写,服务端均会有一个线程做资源响应。缺点:
1:1
正比关系,一次对于高并发场景,线程数量收到硬件上限瓶颈。仅适合学习基本 socket 编程,不适合任何服务器 Server 构建。
① 主线程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 复用机制
阻塞,其他链接过来重复 ②、③流程。
优点:
1:1
与客户端的线程数量关系。缺点:
Client3
占据main thread
流程时,Client1,Client2
流程卡在IO 复用
等待下次监听触发事件。① 主线程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
写给对方。
优点:
模型三
, 将业务处理部分,通过工作池分离出来,减少多客户端访问 Server,业务为串行执行,大量请求会有排队延迟时间。缺点:
main thread
单独处理,最高读写并行通道依然为 1.main thread
的Read + Write
① 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 复用机制(select 、epoll)
,用来监听main thread
建立成功并且分发下来的 socket 套接字。
④ 如图, thread
监听ConnFd1 、ConnFd2
, thread2
监听ConnFd3
,thread3
监听ConnFd4
. 当对应的ConnFd
有读写事件,对应的线程处理该套接字的读写及业务。
优点:
main thread
的单流程读写,分散到多线程完成,这样增加了同一时刻的读写并行通道,并行通道数量N
, N
为线程池Thread
数量。ConnFd 套接字
数量几乎成倍增大,之前的全部监控数量取决于main thread
的多路 I/O 复用机制
的最大限制***(select 默认为 1024,epoll 默认与内存大小相关,约 3~6w 不等)***,所以理论单点 Server 最高响应并发数量为N*(3~6W)
(N
为线程池Thread
数量,建议与 CPU 核心成比例 1:1)。Thread
处理合理业务的效率,降低 CPU 切换成本开销。缺点:
N
,而且多个身处同一个 Thread 的客户端,会出现读写延迟现象,实际上每个Thread
的模型特征与模型三:单线程多路 IO 复用
一致。与五、单线程 IO 复用+多线程 IO 复用(链接线程池)
无大差异。
不同处
main process
(主进程)不再进行Accept
操作,而是将Accept
过程分散到各个子进程(process)
中.main process
如果 Accept 成功的 fd,其他进程无法共享资源,所以需要各子进程自行 Accept 创建链接main process
只是监听ListenFd
状态,一旦触发读事件(有新连接请求). 通过一些 IPC(进程间通信:如信号、共享内存、管道)等, 让各自子进程Process
竞争Accept
完成链接建立,并各自监听。与五、单线程 IO 复用+多线程 IO 复用(链接线程池)
无大差异。
不同处:
多进程内存资源空间占用稍微大一些
多进程模型安全稳定型较强,这也是因为各自进程互不干扰的特点导致。
① 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 复用机制(select 、epoll)
,用来监听main thread
建立成功并且分发下来的 socket 套接字。一旦其中某个被监听的客户端套接字触发I/O 读写事件
,那么,会立刻开辟一个新线程来处理I/O 读写
业务。
④ 但某个读写线程完成当前读写业务,如果当前套接字没有被关闭,那么将当前客户端套接字如:ConnFd3
重新加回线程池的监控线程中,同时自身线程自我销毁。
优点:
在模型五、单线程 IO 复用+多线程 IO 复用(链接线程池)
基础上,除了能够保证同时响应的最高并发数
,又能解决读写并行通道
局限的问题。
同一时刻的读写并行通道,达到最大化极限
,一个客户端可以对应一个单独执行流程处理读写业务,读写并行通道与客户端数量1:1
关系。
缺点:
1:1
的关系,那么 Server 需要开辟的Thread
数量就与客户端一致,那么线程池中做多路 I/O 复用
的监听线程池绑定 CPU 数量将变得毫无意义。Thread
都能够绑定一个单独的 CPU,那么此模型将是最优模型。但是目前 CPU 的数量无法与客户端的数量达到一个量级,目前甚至差的不是几个量级的事。综上,我们整理了 7 中 Server 的服务器处理结构模型,每个模型都有各自的特点和优势,那么对于多少应付高并发和高 CPU 利用率的模型,目前多数采用的是模型五(或模型五进程版,如 Nginx 就是类似模型五进程版的改版)。
至于并发模型并非设计的约复杂越好,也不是线程开辟的越多越好,我们要考虑硬件的利用与和切换成本的开销。模型六设计就极为复杂,线程较多,但以当今的硬件能力无法支撑,反倒导致该模型性能极差。所以对于不同的业务场景也要选择适合的模型构建,并不是一定固定就要使用某个来应用。
###关于作者:
mail: danbing.at@gmail.com github: https://github.com/aceld 原创书籍 gitbook: http://legacy.gitbook.com/@aceld
(原创开源)Zinx-基于 Golang 轻量级服务器并发框架-完整版(附教程视频)
(原创开源)Lars-基于 C++负载均衡远程调度系统-完整版
典藏版-Golang 三色标记、混合写屏障 GC 模式图文全分析
最常用的调试 golang 的 bug 以及性能问题的实践方法?
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.