一 探讨一下同步、异步和阻塞、非阻塞的一些概念
备注: 关于该'篇章',后续会学习'C/C++、操作系统'知识,再来总结归纳
涉及Nginx和Apache的运行原理和一些区别,图文并茂
(1)同步和异步
同步与异步的重点在消息通知的方式上,也就是调用结果的通知方式不同。
通俗:我们在执行某一程序,调用(获取)到程序结果的通知方式不一样!
同步和异步描述的是用户线程与内核的交互方式
同步:当一个同步调用发出去后,调用者要一直等待调用的结果通知后(反馈),才能进行后续的执行。
异步:当一个异步调用发出去后,调用者不必一直等待调用结果的返回(干其它事),当有结果返回,再接受结果!
一般有两种方式
1、主动轮询异步调用的结果--->主动去问,返回了没有,反复的问!
2、被调用方通过 callback(回调通知,回调函数)来通知调用方获取调用结果!
通俗理解:调用方发出异步调用后,等被调用方发送消息了,调用方通知被调用方再去取这个值!
实例解释
同步取快递:小明收到顺风快递将送达的短信,在楼下一直等到快递送达,等到快递送到手里为止!
场景:快递一直没来,只能一直傻等!
异步取快递:小明收到快递将送达的短信,快递到楼下后(消息已经到了),小明再下楼去取
同步缺点:如果你取的消息不给你,会一直占用系统资源!
异步取快递,小明知道快递到达楼下有两种方式:
1、不停的电话问快递小哥到了没有,即主动轮询
不停的问,小哥到那了,快到了,我去取!
主动询问消息到达的位置,等消息到达了再去取!
2、快递小哥到楼下后,打电话通知小明(收到了消息的会调通知),然后小明下楼取快递,即回调通知,常见
收到了快递要到送达的消息,给小哥说快递到达了楼下之后给我打电话,不到楼下不要打电话,小明干别的事!
(2)阻塞和非阻塞
阻塞与非阻塞的重点在于进/线程等待消息时候的行为(action),也就是在等待消息的时候,当前进/线程是挂起状态还是非挂起状态!
阻塞和非阻塞描述的是用户线程调用内核I/O操作的方式
通俗理解:你发起了一个行为,需要别人去做,你干什么?
阻塞:调用在发出去后(发出调用请求),在消息返回之前,当前进/线程会被挂起,直到有消息返回,当前进/线程才会被激活,处理返回的值
非阻塞:调用在发出去后,并不会停下来等待请求的回执,不会阻塞当前进/线程,而会立即返回(干别的事情),当调用的结果返回才激活其它线程或当前线程来处理!
实例
阻塞取快递:小明收到快递即将送达的信息后,什么事都不做,一直专门等快递。
非阻塞取快递:小明收到快递即将送达的信息后,等快递的时候,还一边敲代码、一边刷微信
理解:同步与异步重点在于消息通知的方式;阻塞与非阻塞重点在于等消息时候的行为
所以就有了下面 4 种组合方式
同步阻塞:小明收到信息后,啥都不干,在楼下等快递;
同步非阻塞:小明收到信息后,边刷微博,在楼下(等快递的地方)站着边等着取快递;
异步阻塞:小明收到信息后,在楼上啥都不干,一直等着快递员通知他取快递
异步非阻塞:小明收到信息后,在楼上边刷着微博,边等快递员通知他取快递。
######################################
生产环境中的大部分数据处理模式:同步阻塞(Apache-->select)和异步非阻塞(Nginx-->epoll)
大部分程序的 I/O 模型都是同步阻塞的,单个进程每次只在一个文件描述符上执行 I/O 操作,每次 I/O系统调用都会阻塞,直到完成数据传输,才会完成下一个任务的执行。传统的服务器采用的就是同步阻塞的多进程模型:一个 server采用一个进程负责一个 request 的方式,一个进程负责一个 request,直到会话结束。进程数就是并发数,而操作系统支持的进程数是有限的,且进程数越多,调度的开销(系统资源)也越大,因此同步阻塞(Apache)无法面对高并发
Nginx 采用了异步非阻塞的方式工作。我们先来先了解一下 I/O 多路复用中的 epoll 模型。
epoll 模型:当连接有 I/O 事件产生的时候(当有用户请求时),epoll 就会去告诉进程哪个连接有 I/O 事件产生,然后进程(工作进程把)就去处理这个事件。
例如:小明家楼下有一个收发室,每次有快递到了,门卫就先代收并做了标记(小明的),并且通知小明去取送给小明的快递。
通俗理解:一个用户的请求到达服务器上,首先epell(门卫)会把请求(快递)接收出来,这个时候不一定会有人处理,如果有空闲的工作进程(小明在家),epolll就会调用工作进程(门卫就会通知小明来取快递,小明立刻来取)来处理!
可能的场景:门卫接受的快递(进程)有点多,发现取快递的不一定在家,有些在家(立马来取),有些不在家(处理不过来的情况);当进程数量太多,处理不过来,消息就存储在这个位置,直到有相应的进程来处理为止!
############工作原理########多路复用########
为什么 Nginx 比其他 web 服务器(同步阻塞)并发高?
说明:涉及Nginx 工作原理
Nginx 配置 use epoll 工作模式后,以异步非阻塞方式工作,能够轻松处理百万级的并发连接。
处理过程:每进来一个 request进来,会有相应的一个空闲 worker 进程去处理,但不是全程的处理,处理到可能发生阻塞的地方。比如向后端服务器转发 request,并等待请求返回。那么这个处理的 worker 不会这么傻等着,他会在发送完请求后,注册一个事件:“如果后端服务器返回了,告诉我一声,我再接着干”,于是他就休息去了(释放这个进程)。此时如果再有新的 request 进来,他就可以很快再按这种方式处理。而一旦后端服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走。通过这种快速处理,快速释放请求的方式,达到同样的配置可以处理更大并发量的目的!
IO多路复用的模式:select poll kqueue epoll
IO多路复用:也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型!
##############理解2#################
一般来说,IO操作都分为两个阶段,就拿套接口的输入操作来说,它的两个阶段主要是:
(1)等待网络数据到来,当分组到来时将其拷贝到内核空间的临时缓冲区中
(2)将内核空间临时缓冲区中的数据拷贝到用户空间缓冲区中!
I/O模型:由于进程是不可直接访问外部设备的,所以只能调用内核去调用外部的设备(上下文切换),然后外部设备比如磁盘,读出存储在设备自身的数据传送给内核缓冲区,内核缓冲区在copy数据到用户进程的缓冲区,在外部设备响应的给到用户进程过程中,包含了两个阶段,由于数据响应方式的不同,所以就有了不同的I/O模型
同步是指用户线程发起I/O请求后,需要等待或者轮询内核I/O操作完成时才能继续执行
异步是指用户线程发起I/O请求后仍继续执行,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数