PHP实现非阻塞模式的方法分析


Posted in PHP onJuly 26, 2018

本文实例讲述了PHP实现非阻塞模式的方法。分享给大家供大家参考,具体如下:

程序非阻塞模式,这里也可以理解成并发。而并发又暂且可以分为网络请求并发本地并发

先说一下网络请求并发

理论描述

假设有一个client,程序逻辑是要请求三个不同的server,处理各自的响应。传统模型当然是顺序执行,先发送第一个请求,等待收到响应数据后再发送第二个请求,以此类推。就像是单核CPU,一次只能处理一件事,其他事情被暂时阻塞。而并发模式可以让三个server同时处理各自请求,这就可以使大量时间复用。

画个图更好说明问题:

PHP实现非阻塞模式的方法分析

前者为阻塞模式,忽略请求响应等时间,总耗时为700ms;而后者非阻塞模式,由于三个请求可以同时得到处理,总耗时只有300ms。

代码实现

<?php
echo "Program starts at ". date('h:i:s') . "./n";
$timeout = 3;
$sockets = array(); //socket句柄数组
//一次发起多个请求
$delay = 0;
while ($delay++ < 3)
{
  $sh = stream_socket_client("localhost:80", $errno, $errstr, $timeout,
      STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
  /* 这里需要稍微延迟一下,否则下面fwrite中的socket句柄不一定能真正使用
    这里应该是PHP的一处bug,查了一下,官方bug早在08年就有人提交了
    我的5.2.8中尚未解决,不知最新的5.3中是否修正
  */
  usleep(10);
  if ($sh) {
    $sockets[] = $sh;
    $http_header = "GET /test.php?n={$delay} HTTP/1.0/r/n";
    $http_header .= "Host: localhost/r/n";
    $http_header .= "Accept: */*/r/n";
    $http_header .= "Accept-Charset: */r/n";
    $http_header .= "/r/n";
    fwrite($sh, $http_header);
  } else {
    echo "Stream failed to open correctly./n";
  }
}
//非阻塞模式来接收响应
$result = array();
$read_block_size = 8192;
while (count($sockets))
{
  $read = $sockets;
  $n = stream_select($read, $w=null, $e=null, $timeout);
  //if ($n > 0) //据说stream_select返回值不总是可信任的
  if (count($read))
  {
    /* stream_select generally shuffles $read, so we need to
      compute from which socket(s) we're reading. */
    foreach ($read as $r)
    {
      $id = array_search($r, $sockets);
      $data = fread($r, $read_block_size);
      if (strlen($data) == 0)
      {
        echo "Stream {$id} closes at " . date('h:i:s') . "./n";
        fclose($r);
        unset($sockets[$id]);
      } else {
        if (!isset($result[$id])) $result[$id] = '';
        $result[$id] .= $data;
      }
    }
  } else {
    echo "Time-out!/n";
    break;
  }
}
//print_r($result);

几点说明:

1、使用stream_socket_client函数链接请求服务器和端口(简便起见这里使用同一地址localhost)。这里不受限于http协议,可广泛用于所有TCP/IP协议。详细内容请参考手册。

2、这里链接成功后通过发送各自http头信息来获取不同响应(这里使用网站根目录下的test.php做服务端)。

3、发送header前需要个微小的延迟,代码中已经做了注释。

CLI模式运行结果:

PHP实现非阻塞模式的方法分析

多运行几次会发现,三次请求结束顺序是无序的。该demo太过简单导致整个过程一秒内已完成,但可以针对三次不同请求做相应延迟,来看出非阻塞时时间复用的效果。

下面再大概说下本地并发

本地并发只能通过语言自己的特性在程序本身实现多任务效果,一般来说现在的语言会通过多线程或多进程的方式来实现。由于PHP不支持多线程,目前只能采用多进程方式,让操作系统来帮助实现本地并发。

至于代码实现,可以通过pcntl扩展(封装fork等进程控制函数,和C语言中使用非常相似,windows下不可用)、 proc_openpopen等方式,方法不止一种,这里就不做详细介绍了。详情可自行搜索“php多进程”进行了解:)

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
php设计模式 Chain Of Responsibility (职责链模式)
Jun 26 PHP
PHP原生模板引擎 最简单的模板引擎
Apr 25 PHP
获取PHP警告错误信息的解决方法
Jun 03 PHP
ThinkPHP模板自定义标签使用方法
Jun 26 PHP
基于ThinkPHP实现批量删除
Dec 18 PHP
WordPress开发中短代码的实现及相关函数使用技巧
Jan 05 PHP
我整理的PHP 7.0主要新特性
Jan 07 PHP
Yii模型操作之criteria查找数据库的方法
Jul 15 PHP
thinkPHP模板引擎用法示例
Dec 08 PHP
如何判断php mysqli扩展类是否开启
Dec 24 PHP
详细对比php中类继承和接口继承
Oct 11 PHP
PHP程序员必须知道的两种日志实例分析
May 14 PHP
php实现等比例压缩图片
Jul 26 #PHP
PHP输出Excel PHPExcel的方法
Jul 26 #PHP
PHP微信H5支付开发实例
Jul 25 #PHP
php app支付宝回调(异步通知)详解
Jul 25 #PHP
php支付宝APP支付功能
Jul 29 #PHP
PHP多个图片压缩成ZIP的方法
Aug 18 #PHP
PHP上传文件及图片到七牛的方法
Jul 25 #PHP
You might like
在PHP中利用XML技术构造远程服务(上)
2006/10/09 PHP
手把手教你使用DedeCms V3的在线采集图文教程
2007/04/03 PHP
PHP 小心urldecode引发的SQL注入漏洞
2011/10/27 PHP
php使用glob函数快速查询指定目录文件的方法
2014/11/15 PHP
一组JS创建和操作表格的函数集合
2009/05/07 Javascript
jQuery Ajax文件上传(php)
2009/06/16 Javascript
JavaScript 未结束的字符串常量常见解决方法
2010/01/24 Javascript
让ie运行js时提示允许阻止内容运行的解决方法
2010/10/24 Javascript
jQuery 一个图片切换的插件
2011/10/09 Javascript
用JavaScript实现一个代码简洁、逻辑不复杂的多级树
2014/05/23 Javascript
js 通过cookie实现刷新不变化树形菜单
2014/10/30 Javascript
javascript限制文本框输入值类型的方法
2015/05/07 Javascript
每天一篇javascript学习小结(Array数组)
2015/11/11 Javascript
浅析JS获取url中的参数实例代码
2016/06/14 Javascript
JS输出空格的简单实现方法
2016/09/08 Javascript
JS中Array数组学习总结
2017/01/18 Javascript
jQuery初级教程之网站品牌列表效果
2017/08/02 jQuery
详解require.js配置路径的用法和css的引入
2017/09/06 Javascript
解决jquery appaend元素中id绑定事件失效的问题
2017/09/12 jQuery
js实现手机web图片左右滑动效果
2017/12/29 Javascript
原生JS实现逼真的图片3D旋转效果详解
2019/02/16 Javascript
vuex存储复杂参数(如对象数组等)刷新数据丢失的解决方法
2019/11/05 Javascript
vue父子模板传值问题解决方法案例分析
2020/02/26 Javascript
js实现带积分弹球小游戏
2020/07/21 Javascript
详解node.js 事件循环
2020/07/22 Javascript
vue data对象重新赋值无效(未更改)的解决方式
2020/07/24 Javascript
React冒泡和阻止冒泡的应用详解
2020/08/18 Javascript
[57:59]EG vs Secret 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
Python读取键盘输入的2种方法
2015/06/16 Python
python如何修改装饰器中参数
2018/03/20 Python
Python安装图文教程 Pycharm安装教程
2018/03/27 Python
详解Django的CSRF认证实现
2018/10/09 Python
python实现QQ空间自动点赞功能
2019/04/09 Python
Django数据库类库MySQLdb使用详解
2019/04/28 Python
Pandas的Apply函数具体使用
2020/07/21 Python
经典的毕业生自荐信范文
2014/04/14 职场文书