共计 1146 个字符,预计需要花费 3 分钟才能阅读完成。
阻塞模型限制了服务器的并发处理能力(伸缩性或同时处理的客户端连接数)
传统的网络服务器只支持阻塞模型,该模型下,针对每个客户端连接,服务器都必须创建一个线程来处理这个连接上的请求,服务器必须维持着这些线程直到线程中的处理工作结束。
服务器上所能创建的线程数量是有限的,WHY?
进程上下文切换是耗时的过程
创建的进程本身占用资源,比如每个进程或线程占用一定容量的内存
等待数据准备和内核缓存复制,导致 IO 阻塞,占用着线程
所以当连接到服务器上的客户端的数量很大时,把服务器上所能创建的线程都占据了时,服务器就无法接受更多的连接了。这限制了服务器处理请求的伸缩性。
并非所有客户端都是持续活跃的
存在这样一个事实,就是虽然连接到服务器上的客户端很多,但并非所有客户端都是持续活跃着的。它们占据着阻塞式服务器的线程资源——即使它们处于非工作状态。这些线程占据了资源,却不工作。
这会造成什么现象呢?就是线程时间的碎片化——一个线程大部分时间是在等待 IO 操作的结果。
为了让服务器能接受更多客户端的连接,非阻塞模型就出现了。
如何提升服务器的并发处理能力?
消灭碎片化时间,可以提升服务器的并发处理能力。
如何消灭碎片化时间?让线程分工协作各司其职,是一个很好的手段。原来的阻塞模型下,一个线程要干所有的事情。分工协作机制下,一部分线程专门用于接受客户端的连接、一部分专门用于获取请求的数据、一部分专门执行计算工作、还有一部分线程专门用于响应客户端。接受客户端连接的线程在接收到客户端连接后,立即把连接交给后续工序的线程处理,而它自己则继续接受下一个连接。如此类推,各个线程无须等待,不存在碎片化时间,全负荷工作。这样一来,整体上需要的较少的线程,就可以完成以前需要较多线程才能达到的工作时间了。
阻塞模型下的实现方式
在阻塞模型下,利用异步处理的方式对线程进行分工协作。接收请求的线程可以满负荷工作,但处理 IO 操作的线程仍然是阻塞着的,仍然存在线程工作不饱和的现象。
非阻塞模型彻底消灭线程工作不饱和
非阻塞模型下,IO 操作不再是阻塞的了,而是立即返回。这样的话,处理 IO 操作的线程,可以在空闲时对所有请求进行轮询,以便判断哪些 IO 操作已完成。比如判断某个请求是否可以进行“写”操作,如果还不可以,无须等待,继续判断下一个请求是否可以进行“读”操作,如果可以则立即读取数据,然后把数据转交给专职计算的线程。这样就让线程工作不饱和现象消失了。
这是所谓的“同步非阻塞”。
轮询的耗时如何消灭?
这就要请出“IO 复用”这尊大神了。
IO 复用模型下,线程一次性从操作系统那儿获得一批可以进行 IO 操作的请求,处理完毕后,再此获得新的一批。线程无须与操作系统交互多次以便轮询每个请求的状态,而是与操作系统交互一次即可获得批量信息。效率进一步提高啦。