PHP实现IO复用模型(multiplexing)

简介: 在 I/O 多路复用模型中,会用到 select 或 poll 函数, 这两个函数也会使进程阻塞,但是和阻塞 I/O 所不同的是,这两个函数可以同时阻塞多个 I/O 操作,而且可以同时对多个读操作、多个写操作的 I/O 函数进行检测,直到有数据可读或可写时,才真正调用 I/O 操作函数。

参考文献

5种IO模型:https://blog.mailjob.net/posts/3565199751.html

github代码下载:https://github.com/mailjobblog/dev_php_io/tree/master/test/multiplexing

Epoll 多路复用是如何转起来的:https://mp.weixin.qq.com/s/Py2TE9CdQ92fGLpg-SEj_g

IO多路复用原理

简单解读

在 I/O 多路复用模型中,会用到 select 或 poll 函数, 这两个函数也会使进程阻塞,但是和阻塞 I/O 所不同的是,这两个函数可以同时阻塞多个 I/O 操作,而且可以同时对多个读操作、多个写操作的 I/O 函数进行检测,直到有数据可读或可写时,才真正调用 I/O 操作函数。

从流程上来看,使用 select 函数进行 I/O 请求和同步阻塞模型没有太大的区别,甚至还多了添加监视 socket,以及调用 select 函数的额外操作,效率更差。但是,使用 select 以后最大的优势是用户可以在一个线程内同时处理多个 socket 的 I/O 请求。用户可以注册多个 socket,然后不断地调用 select 读取被激活的 socket,即可达到在同一个线程内同时处理多个 I/O 请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

图片

IO多路复用详解(可略过直接看代码)

非阻塞情况下无可用数据时,应用程序每次轮询内核看数据是否准备好了也耗费CPU,能否不让它轮询,当内核缓冲区数据准备好了,以事件通知当机制告知应用进程数据准备好了呢?应用进程在没有收到数据准备好的事件通知信号时可以忙写其他的工作。此时IO多路复用就派上用场了。

IO多路复用中文比较让人头大,IO多路复用的原文叫 I/O multiplexing,这里的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流. 发明它的目的是尽量多的提高服务器的吞吐能力。实现一个线程监控多个IO请求,哪个IO有请求就把数据从内核拷贝到进程缓冲区,拷贝期间是阻塞的!现在已经可以通过采用mmap地址映射的方法,达到内存共享效果,避免真复制,提高效率。

图片

select、poll、epoll 都是I/O多路复用的具体的实现。

select

select是第一版IO复用,提出后暴漏了很多问题。

  1. select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。
  2. select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但不会告诉是那个sock上有数据,只能自己遍历查找。
  3. select 只能监视1024个链接。
  4. select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现这个sock不用,要收回,这个select 不支持的。

poll

poll 修复了 select 的很多问题。

  1. poll 去掉了1024个链接的限制。
  2. poll 从设计上来说不再修改传入数组。

但是poll仍然不是线程安全的, 这就意味着不管服务器有多强悍,你也只能在一个线程里面处理一组 I/O 流。你当然可以拿多进程来配合了,不过然后你就有了多进程的各种问题。

epoll

epoll 可以说是 I/O 多路复用最新的一个实现,epoll 修复了poll 和select绝大部分问题, 比如:

  1. epoll 现在是线程安全的。
  2. epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了。
  3. epoll 内核态管理了各种IO文件描述符, 以前用户态发送所有文件描述符到内核态,然后内核态负责筛选返回可用数组,现在epoll模式下所有文件描述符在内核态有存,查询时不用传文件描述符进去了。

三者对比

图片

横轴 Dead connections 是链接数的意思,叫这个名字只是它的测试工具叫deadcon。纵轴是每秒处理请求的数量,可看到epoll每秒处理请求的数量基本不会随着链接变多而下降的。poll 和/dev/poll 就很惨了。但 epoll 有个致命的缺点是只有linux支持。

比如平常Nginx为何可以支持4W的QPS是因为它会使用目标平台上面最高效的I/O多路复用模型。

原生PHP代码演示

服务端中主要用到了 stream_select 方法,通过 select 方法查找出可以操作的文件描述符,对其进行读写操作。

server.php

<?php
require_once __DIR__."/../../vendor/autoload.php";

use DevPhpIO\Multiplexing\Worker;

$server = new Worker('0.0.0.0', 9500);
$server->on('connect', function($server, $client){
   
    dd($client, "客户端成功建立连接");
});

$server->on('receive', function(Worker $server, $client, $data){
   
    dd($data, "处理client的数据");
    $server->send($client, "hello i’m is server");
    // $server->close($client);
}); 

$server->on('close', function($server, $client){
   
    dd($client, "连接断开");
});

$server->start();

client.php

<?php
require_once __DIR__."/../../vendor/autoload.php";

// 连接服务端
$fp = stream_socket_client("tcp://127.0.0.1:9500");
fwrite($fp, "hello world");
dd(fread($fp, 65535));

// 这里阻塞 10s 是为了便于演示
sleep(10);

fwrite($fp, "第二个消息");
dd(fread($fp, 65535));

演示截图

image-20210430174408411

image-20210430174432852

相关文章
|
2月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
99 0
|
2月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
115 1
Linux C/C++之IO多路复用(aio)
|
2月前
|
网络协议 前端开发 Java
网络协议与IO模型
网络协议与IO模型
137 4
网络协议与IO模型
|
2月前
|
安全 NoSQL Java
一文搞懂网络通信的基石✅IO模型与零拷贝
【10月更文挑战第1天】本文深入探讨了网络通信中的IO模型及其优化方法——零拷贝技术。首先介绍了IO模型的概念及五种常见类型:同步阻塞、同步非阻塞、多路复用、信号驱动和异步IO模型。文章详细分析了每种模型的特点和适用场景,特别是多路复用和异步IO在高并发场景中的优势。接着介绍了零拷贝技术,通过DMA直接进行数据传输,避免了多次CPU拷贝,进一步提升了效率。最后总结了各种模型的优缺点,并提供了相关的代码示例和资源链接。
一文搞懂网络通信的基石✅IO模型与零拷贝
|
2月前
|
开发者
什么是面向网络的IO模型?
【10月更文挑战第6天】什么是面向网络的IO模型?
24 3
|
2月前
|
数据挖掘 开发者
网络IO模型
【10月更文挑战第6天】网络IO模型
48 3
|
2月前
|
缓存 Java Linux
硬核图解网络IO模型!
硬核图解网络IO模型!
|
2月前
|
数据挖掘 开发者
网络IO模型如何选择?
网络IO模型如何选择?【10月更文挑战第5天】
23 2
|
2月前
|
数据库
同步IO模型是一种常见的编程模型
【10月更文挑战第5天】同步IO模型是一种常见的编程模型
23 2
|
2月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
38 0
Linux C/C++之IO多路复用(poll,epoll)