最近阅读某大牛的公众号时,其讲Kafka请求全流程和源码时又提到了熟悉又陌生的Reactor模式。熟悉是因为可以说基本上只要底层是高性能网络通信就一定和Reactor模式息息相关,比如Netty,Redis等底层都是Reactor模式。陌生是仍不能探其究竟、知其本真。
早期的网络编程当一个请求过来了,要么当前线程直接就处理了要么另起一个新的线程去处理,一个线程很容易处理不过来且又没有充分利用计算机资源,而每次新起一个线程则对计算机资源要求太高。没错,线程池是一个很好的选择,池化技术确实能缓解资源问题。但池子也是有限的,池子里的线程也是在候着连接等待指示。现在的互联网早就突破C10K(concurrent 10000 connection),应对C10K问题的关键在于充分利用计算机资源、减少CPU消耗。故,引入I/O多路复用,由一个线程监控一堆连接,同步等待一个或多个I/O事件的到来,分配给对应的handler处理,即Reactor模式。
网络通信模型的发展线路:
单线程 => 多线程 => 线程池 => Reactor模型
讲那么多,就是想说理解I/O模型和I/O多路复用很重要。呵呵。
C10K问题本质上是操作系统的问题,故还得从操作系统入手。
本节先理解操作系统(linux)的User space用户空间和Kernel space内核空间。
简单来说,Kenel space是linux的内核运行空间,User space是用户程序的运行空间。安全起见,它们是相互隔离的,当用户程序崩溃时内核不受影响。在内核空间可以执行任何命令、调用系统的一切资源;用户空间内只能执行简单的运算,不能直接调用系统资源,须通过系统接口才能向内核发出指令。通过系统接口,进程可以从用户空间切换到内核空间。
举例:
str = "something nice weights light" // 用户空间
x = x + 1
file.write(str) // 切换到内核空间
y = x + 100 // 切换回用户空间
上面代码,第一行、第二行都是简单的赋值运算直接用户空间执行,第三行用户不能直接写文件须切换到内核空间通过内核安排,第四行又是赋值运算则切换回用户空间。
在linux系统中可通过top命令查看CPU时间片在用户空间和内核空间的分配情况:
上图红色框中的各指标就是CPU的时间分配统计,其中us(user)即CPU消耗在User space的时间占比,sy(system)即CPU消耗在Kernel space的时间占比,其他6个指标含义:
ni:niceness 的缩写,CPU 消耗在 nice 进程(低优先级)的时间百分比
id:idle 的缩写,CPU 消耗在闲置进程的时间百分比,这个值越低,表示 CPU 越忙
wa:wait 的缩写,CPU 等待外部 I/O 的时间百分比,这段时间 CPU 不能干其他事,但是也没有执行运算,这个
值太高就说明外部设备有问题
hi:hardware interrupt 的缩写,CPU 响应硬件中断请求的时间百分比
si:software interrupt 的缩写,CPU 响应软件中断请求的时间百分比
st:stole time 的缩写,该项指标只对虚拟机有效,表示分配给当前虚拟机的 CPU 时间之中,被同一台物理机上
的其他虚拟机偷走的时间百分比。
To Be Continued…
大表哥的剑
2020.8.29