Socket Server的N种并发模型汇总
Posted Go语言中文网
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Socket Server的N种并发模型汇总相关的知识,希望对你有一定的参考价值。
本文主要介绍常见的Server的并发模型,这些模型与编程语言本身无关,有的编程语言可能在语法上直接透明了模型本质,所以开发者没必要一定要基于模型去编写,只是需要知道和了解并发模型的构成和特点即可。
在了解并发模型之前,我们需要两个必备的前置知识:
socket网络编程
多路IO复用机制
多线程/多进程等并发编程理论
提纲
模型一
单线程Accept(无I/O复用)
模型二
单线程Accept+多线程读写业务(无I/O复用)
模型三
单线程多路I/O复用
模型四
单线程多路I/O复用+多线程读写业务(业务工作池)
模型五
单线程多路I/O复用+多线程多路I/O复用(线程池)
模型五(进程版)
单进程多路I/O复用+多进程多路I/O复用(进程池)
模型六
单线程多路I/O复用+多线程多路I/O复用+多线程
模型一、单线程Accept(无I/O复用)
01
02
main thread
执行阻塞Accept,每次客户端Connect链接过来,
main thread
中accept响应并建立连接
Connfd1
套接字后, 依然在
main thread
串行处理套接字读写,并处理业务。
Connect
过来,
Server
无响应,直到当前套接字全部业务处理完毕。
03
socket编程流程清晰且简单,适合学习使用,了解socket基本编程流程。
该模型并非并发模型,是串行的服务器,同一时刻,监听并响应最大的网络请求量为
1
。即并发量为1
。仅适合学习基本socket编程,不适合任何服务器Server构建。
模型二、单线程Accept+多线程读写业务(无I/O复用)
01
02
main thread
执行阻塞Accept,每次客户端Connect链接过来,
main thread
中accept响应并建立连接
Connfd1
套接字后,创建一个新线程
thread1
用来处理客户端的读写业务。
main thead
依然回到
Accept
阻塞等待新客户端。
thread1
通过套接字
Connfd1
与客户端进行通信读写。
Connect
过来,
main thread
中
Accept
依然响应并建立连接,重复②过程。
03
基于
模型一:单线程Accept(无IO复用)
支持了并发的特性。使用灵活,一个客户端对应一个线程单独处理,
server
处理业务内聚程度高,客户端无论如何写,服务端均会有一个线程做资源响应。
随着客户端的数量增多,需要开辟的线程也增加,客户端与server线程数量
1:1
正比关系,一次对于高并发场景,线程数量受到硬件上限瓶颈。对于长链接,客户端一旦无业务读写,只要不关闭,server的对应线程依然需要保持连接(心跳、健康监测等机制),占用连接资源和线程开销资源浪费。
仅适合客户端数量不大,并且数量可控的场景使用。
模型三、单线程多路I/O复用
01
02
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将没有即时响应。
Read+Write
操作,继续回到
多路I/O复用机制
阻塞,其他链接过来重复 ②、③流程。
03
单流程解决了可以同时监听多个客户端读写状态的模型,不需要
1:1
与客户端的线程数量关系。多路I/O复用阻塞,非忙轮询状态,不浪费CPU资源, CPU利用率较高。
虽然可以监听多个客户端的读写状态,但是同一时间内,只能处理一个客户端的读写操作,实际上读写的业务并发为1。
多客户端访问Server,业务为串行执行,大量请求会有排队延迟现象,如图中⑤所示,当
Client3
占据main thread
流程时,Client1,Client2
流程卡在IO复用
等待下次监听触发事件。
模型四、单线程多路I/O复用+多线程读写业务
(业务工作池)
01
02
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
写给对方。
03
对于
模型三
, 将业务处理部分,通过工作池分离出来,减少多客户端访问Server,业务为串行执行,大量请求会有排队延迟时间。实际上读写的业务并发为1,但是业务流程并发为worker pool线程数量,加快了业务处理并行效率。
读写依然为
main thread
单独处理,最高读写并行通道依然为1.虽然多个worker线程处理业务,但是最后返回给客户端,依旧需要排队,因为出口还是
main thread
的Read + Write
模型五、单线程I/O复用+多线程I/O复用
(线程池)
01
02
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
有读写事件,对应的线程处理该套接字的读写及业务。
03
将
main thread
的单流程读写,分散到多线程完成,这样增加了同一时刻的读写并行通道,并行通道数量N
,N
为线程池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复用
一致。
模型五(进程版)、单进程多路I/O复用+多进程多路I/O复用
(进程池)
01
02
五、单线程IO复用+多线程IO复用(链接线程池)
无大差异。
进程和线程的内存布局不同导致,
main process
(主进程)不再进行Accept
操作,而是将Accept
过程分散到各个子进程(process)
中.进程的特性,资源独立,所以
main process
如果Accept成功的fd,其他进程无法共享资源,所以需要各子进程自行Accept创建链接main process
只是监听ListenFd
状态,一旦触发读事件(有新连接请求). 通过一些IPC(进程间通信:如信号、共享内存、管道)等, 让各自子进程Process
竞争Accept
完成链接建立,并各自监听。
03
五、单线程IO复用+多线程IO复用(链接线程池)
无大差异。
模型六、单线程多路I/O复用+多线程多路I/O复用+多线程
01
02
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
重新加回线程池的监控线程中,同时自身线程自我销毁。
03
在
模型五、单线程IO复用+多线程IO复用(链接线程池)
基础上,除了能够保证同时响应的最高并发数
,又能解决读写并行通道
局限的问题。同一时刻的读写并行通道,达到
最大化极限
,一个客户端可以对应一个单独执行流程处理读写业务,读写并行通道与客户端数量1:1
关系。
该模型过于理想化,因为要求CPU核心数量足够大。
如果硬件CPU数量可数(目前的硬件情况),那么该模型将造成大量的CPU切换成本浪费。因为为了保证读写并行通道与客户端
1:1
的关系,那么Server需要开辟的Thread
数量就与客户端一致,那么线程池中做多路I/O复用
的监听线程池绑定CPU数量将变得毫无意义。如果每个临时的读写
Thread
都能够绑定一个单独的CPU,那么此模型将是最优模型。但是目前CPU的数量无法与客户端的数量达到一个量级,目前甚至差的不是几个量级的事。
总结
推荐阅读
喜欢本文的朋友,欢迎关注“Go语言中文网”:
以上是关于Socket Server的N种并发模型汇总的主要内容,如果未能解决你的问题,请参考以下文章
Python并发编程-IO模型-非阻塞IO实现SocketServer