聊聊nginx的高并发原理

知识点

同步异步

类似ajax里的sync同步和异步。

同步: 当一个同步调用发出去后,调用者要一直等待调用结果的通知,直到得到调用结果。
异步: 当一个异步调用发出去后,调用者不能立即得到调用结果的通知

所以异步调用,要想得到结果,一般有2种方式:
1,主动轮询异步调用的结果。得一个个问
2,被调用方通过callback来通知调用发调用结果。不需要一个个问,他们会主动告诉你。

阻塞与非阻塞

阻塞: 当一个阻塞调用发出去后,在消息返回之前,当前进程或线程会被挂起,直到有消息返回,当前进程线程才会被激活。

非阻塞: 当一个非阻塞调用发出去后,不会阻塞当前进/线程,而会立即返回。

好像跟同步异步差不多。

其实有一点区别.

1
2
同步与异步: 重点在于消息通知的方式。
阻塞与非阻塞:重点在于等消息时候的行为。

以小明买奶茶为例:

同步: 小明主动去问店员 奶茶做好了没,直到奶茶做好
异步:等奶茶做好了,店员叫号通知小明去取奶茶
阻塞:小明买了奶茶,啥事都不做,在那干等着奶茶做好
非阻塞: 小明买了奶茶,在等的过程中 时不时的聊微信 刷微博

所以有了下面4种组合方式:
同步阻塞: 小明在柜台干等着拿奶茶
同步非阻塞: 小明在柜台边等边刷微博等着拿奶茶
异步阻塞: 小明拿着小票啥都不干,等着店员通知他去拿奶茶
异步非阻塞: 小明拿着小票,聊微信 刷微博,等着店员通知他去拿奶茶。

nginx如何处理高并发

apache处理一个请求事同步阻塞的模式。
每到达一起请求,就需要fork一个子进程去处理,直到这个子进程处理完毕。
如果是高并发,一个客户端占用一个进程,如果10000次的话 就得fork 10000个进程去处理。比如著名的c10k问题。

1
什么是C10K问题?网络服务在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪,这被称为C10K问题。(concurrent 10000 connection)

那么有没有一种方式,可以让我们在一个进程处理所有的并发I/O呢?
有的,这就是I/O复用技术。

I/O复用是什么

所谓的I/O复用,就是多个I/O可以复用同一个进程。

可以考虑非阻塞的方式。
当一个连接过来时,我们不阻塞住,这样一个进程可以同时处理多个连接了。

比如一个进程接收了10000个连接,这个进程每次从头到尾的问一遍这10000个连接:’有I/O事件没’?有的话就给我处理,没有的话我到时候再来问一遍。
然后进程就一直从头到尾问这10000个连接,如果这10000个连接都没有I/O事件,就会造成cpu的空转,效率很低。

那如何解决呢?
我们能不能引入一个代理,这个代理可以同时观察许多I/O流事件呢?

于是早期的程序员们就发明了两个代理- selectpoll

selectpoll的原理是这样的:
当连接有I/O流事件产生的时候,就会去唤醒进程去处理。
但是进程并不知道是哪个连接产生的I/O事件,于是进程就挨个去问:’请问是你有事来处理吗?’。。。。问了99999遍,哦,原来是第100000个进程有事要处理,那前面99999次就白问了,白白浪费了cpu的时间片。

所以有没有一种代理,每次能够知道是哪里连接有I/O事件呢,不就可以避免cpu无意义的空转了吗?

于是目前最流行的epoll就被发明出来了。
epoll代理的原理是这样的:
当连接有I/O事件发生的时候,epoll就会去告诉进程哪个连接有I/O事件发生,然后进程就去处理。

这样的话,多么高效!

基于epoll的nginx

有了epoll,理论上1个进程就可以无限数量的连接,而且无需轮询,真正解决了c10k的问题。

Nginx是基于epoll的,异步非阻塞的服务器程序。
自然,nginx就能轻松处理百万级的并发连接,也就无可厚非了

swoole是如何处理高并发以及异步I/O的

swoole是php的一个扩展。
PHP的异步、并行、高性能网络通信引擎,支持TCP长连接,Websocket,Mqtt等协议
swoole = 异步I/O+网络通信。
简直是神器,phper可以利用swoole去开发手游服务端、网络游戏服务器、聊天室等等需要网络通信的应用程序。

可以看swoole的官网。

swoole采用 多线程Reactor + 多进程Worker.
swoole的worker进程有2种类型:
1,普通的worker进程
2,task worker进程

worker进程用来处理普通的耗时不是太长的请求。
tast worker进程用来处理耗时较长的请求,比如数据库的I/O操作。

异步mysql请求到达时,swoole这样处理:
1,耗时较长的mysql查询进入worker
2,worker通过管道将这个请求交给tast worker来处理
3,worker再去处理其他请求
4,tast worker处理完毕后,处理结果通过管道返回给worker
5,worker将结果返回给Reactor
6,Reactor将结果返回给请求方

Reactor模型介绍

I/O复用异步非阻塞程序使用经典的Reactor模型。
Reactor就是反应堆的意思,它本身不处理任何数据收发,只是可以监听一个socket(也可以是管道,eventfd,信号)句柄的事件变化。

句柄就是handler,也就是资源的唯一标识符,资源的id。通过这个id可以操作该资源。

1
2
3
4
Add: 添加一个socket到Reactor
Set: 修改socket对应的事件,如可读可写
Del: 从Reactor移除
Callback: 事件发生后回调指定的函数

Reactor只是一个事件发生器,实际对socket句柄的操作,如connect/accept,send/recv,close 是在callback中完成的。

请求到达时,Reactor是这样处理的:
1,请求到达 main Reactor。
2,main Reactor根据socket的情况,将请求注册给对应的Reactor(每个Reactor都有epoll。用来监听客户端的变化)
3,客户端变化时,交给worker来处理
4,worker处理完毕,通过进程间通信(比如管道,共享内存,消息队列) 发给对应的Reactor。
5,Reactor将相应结果发给相应的连接
6,请求处理完成

坚持原创技术分享,您的支持将鼓励我继续创作!