Swoole网络模型(三)

Swoole网络模型(三)


网络服务模型

1)单进程阻塞的网络服务器 ,如下图

file 说明:

1、创建一个socket,绑定服务器端口(bind),监听端口(listen),在PHP中用stream_socket_server一个函数就能完成上面3个步骤
2、进入while循环,阻塞在accept操作上,等待客户端连接进入。此时程序会进入睡眠状态,直到有新的客户端发起connect到服务器,操作系统会唤醒此进程。accept函数返回客户端连接的socket
3、利用fread读取客户端socket当中的数据收到数据后服务器程序进行处理然后使用fwrite向客户端发送响应。长连接的服务会持续与客户端交互,而短连接服务一般收到响应就会close。

缺点:

1、一次只能处理一个连接,不支持多个连接同时处理 

2)预派生子进程模式

file

说明:

前面流程一致
1、程序启动后就会创建N个进程。每个子进程进入Accept,等待新的连接进入。当客户端连接到服务器时,其中一个子进程会被唤醒,开始处理客户端请求,并且不再接受新的TCP连接。当此连接关闭时,子进程会释  放,重新进入Accept,参与处理新的连接。
这个模型的优势是完全可以复用进程,不需要太多的上下文切换,比如php-fpm基于此模型的。

缺点:

1、这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的。

 例如:即时聊天程序,一台服务器可能要维持数十万的连接,那么就要启动数十万的进程来维持。这显然不可能

基于上面的模式我们发现我们只能通过每次(accept)处理单个请求,没办法一次性处理多个请求?

3)单进程阻塞复用的网络服务器 ,如下图

file

说明:

服务监听流程如上
1、保存所有的socket,通过select系统调用,监听socket描述符的可读事件
2、Select会在内核空间监听一旦发现socket可读,会从内核空间传递至用户空间,在用户空间通过逻辑判断是服务端socket可读,还是客户端的socket可读
3、如果是服务端的socket可读,说明有新的客户端建立,将socket保留到监听数组当中
4、如果是客户端的socket可读,说明当前已经可以去读取客户端发送过来的内容了,读取内容,然后响应给客户端。

缺点:

    1、select模式本身的缺点(1、循环遍历处理事件、2、内核空间传递数据的消耗)单进程对于大量任务处理乏力

4)多进程master-worker模型

file

1、master进程,负责处理配置文件读取,启动,终止和维护工作(worker)进程数,当woker进程退出后(异常情况下),会自动重新启动新的woker

2、worker进程的主要任务是完成具体的任务逻辑,启动端口监听,接收客户端请求、使用epoll接收请求,执行业务逻辑然后关闭连接。

    其中对于进程重新启动需要通过发送信号来实现 

IO复用/EventLoop


什么是IO复用

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,目前支持I/O多路复用有 select,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

Select跟poll

Select介绍:

监视并等待多个文件描述符的属性变化(可读、可写或错误异常)。select函数监视的文件描述符分 3 类,分别是writefds、readfds、和 exceptfds。调用后 select会阻塞,直到有描述符就绪(有数据可读、可写、或者有错误异常),或者超时( timeout 指定等待时间),函数才返回。当 select()函数返回后,可以通过遍历 fdset,来找到就绪的描述符,并且描述符最大不能超过1024

poll 介绍:

    poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

问题:

    select/poll问题很明显,它们需要循环检测连接是否有事件。如果服务器有上百万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll需要做循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,浪费CPU资源。

epoll

  epoll是select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制,无需轮询。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中。 简单点来说就是当连接有I/O流事件产生的时候,epoll就会去告诉进程哪个连接有I/O流事件产生,然后进程就去处理这个事件。

epoll采用了高效的事件处理模式Reactor 模式

Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket句柄的事件变化。
1)  主进程/线程往epoll内核亊件中注册socket上的读就绪亊件。
2)  主进程/线程调用epoll_wait等待socket上有数据可读。
3)  当socket上有数据可读时,epoll_wait通知主进程/线程。主进程/线程则将socket可读事件放人请求队列。
4)  睡眠在请求队列上的某个工作线程被唤醒,它从socket读取数据,并处理客户请求, 然后往epoll内核事件表中注册该socket上的写就绪事件。
5)  主线程调用epoll_wait等待socket可写。
6)  当socket可写时,epoll_wait通知主进程/线程将socket可写亊件放人清求队列。
7)  睡眠在请求队列上的某个工作线程被唤醒,它往socket上写人服务器处理客户淸求

file

注意:

Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间,比如在大文件传输时,IO操作就会影响其他Client的相应时间,因而对这种操作,使用传统的Thread-Per-Connection或许是一个更好的选择,或则此时使用Proactor模式。
猜你喜欢