php执行一段程序,有可能几毫秒就执行完毕,也有可能耗时较长。例如,用户下单这个事件,如果调用了些第三方服务进行发邮件、短信、推送等通知,可能导致前端一直在等待。而有的时候,我们并不关心这些耗时脚本的返回结果,只要执行就行了。这时候就需要采用异步的方式执行。

众所周知,PHP没有直接支持多线程这种东西。我们可以采用折衷的方式实现。这里主要说的就是fsockopen。

通过fsockopen发送请求并忽略返回结果,程序可以马上返回。

示例代码:

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET /backend.php HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out);
/*忽略执行结果
while (!feof($fp)) {
echo fgets($fp, 128);
}*/
fclose($fp);
}

需要注意的是我们需要手动拼出header头信息。通过打开注释部分,可以查看请求返回结果,但这时候又变成同步的了,因为程序会等待返回结果才结束。

实际测试的时候发现,不忽略执行结果,调试的时候每次都会成功发送sock请求;但忽略执行结果,经常看到没有成功发送sock请求。查看nginx日志,发现很多状态码为499的请求。

后来找到了原因:fwrite之后马上执行fclose,nginx会直接返回499,不会把请求转发给php处理。

客户端主动端口请求连接时,NGINX 不会将该请求代理给上游服务(FastCGI PHP 进程),这个时候 access log 中会以 499 记录这个请求。

解决方案:

1)nginx.conf增加配置

# 忽略客户端中断
fastcgi_ignore_client_abort on;

2)fwrite之后使用usleep函数休眠20毫秒:

usleep(20000);

后来测试就没有发现失败的情况了。

附上完整代码:

<?php
/**
* 工具类
* */
class FsockService { public static function post($url, $param){ $host = parse_url($url, PHP_URL_HOST);
$port = 80;
$errno = '';
$errstr = '';
$timeout = 30; $data = http_build_query($param); // create connect
$fp = fsockopen($host, $port, $errno, $errstr, $timeout); if(!$fp){
return false;
} // send request
$out = "POST ${url} HTTP/1.1\r\n";
$out .= "Host:${host}\r\n";
$out .= "Content-type:application/x-www-form-urlencoded\r\n";
$out .= "Content-length:".strlen($data)."\r\n";
$out .= "Connection:close\r\n\r\n";
$out .= "${data}"; fwrite($fp, $out); //忽略执行结果;否则等待返回结果
// if(APP_DEBUG === true){
if(false){
$ret = '';
while (!feof($fp)) {
$ret .= fgets($fp, 128);
}
} usleep(20000); //fwrite之后马上执行fclose,nginx会直接返回499 fclose($fp);
} public static function get($url, $param){
$host = parse_url($url, PHP_URL_HOST);
$port = 80;
$errno = '';
$errstr = '';
$timeout = 30; $url = $url.'?'.http_build_query($param); // create connect
$fp = fsockopen($host, $port, $errno, $errstr, $timeout); if(!$fp){
return false;
} // send request
$out = "GET ${url} HTTP/1.1\r\n";
$out .= "Host:${host}\r\n";
$out .= "Connection:close\r\n\r\n"; fwrite($fp, $out); //忽略执行结果;否则等待返回结果
// if(APP_DEBUG === true){
if(false){
$ret = '';
while (!feof($fp)) {
$ret .= fgets($fp, 128);
}
} usleep(20000); //fwrite之后马上执行fclose,nginx会直接返回499 fclose($fp);
} } ?>

参考:

1、PHP实现异步调用方法研究 | 风雪之隅

http://www.laruence.com/2008/04/14/318.html

2、PHP fsockopen 异步调用接口在nginx上偶尔实效的情况 - 张炜施 - 博客园

http://www.cnblogs.com/zhangweishi/p/5306813.html

3、php 利用fsockopen GET/POST 提交表单及上传文件 - 傲雪星枫 - 博客频道 - CSDN.NET

http://blog.csdn.net/fdipzone/article/details/11712607

4、关于PHP的异步调用 - 浮云比翼 - 博客园

http://www.cnblogs.com/fuyunbiyi/archive/2013/03/27/2985089.html

php中使用fsockopen实现异步请求的更多相关文章

  1. 微信小程序中使用Async-await方法异步请求变为同步请求

    微信小程序中有些 Api 是异步的,无法直接进行同步处理.例如:wx.request.wx.showToast.wx.showLoading等.如果需要同步处理,可以使用如下方法: 注意: Async ...

  2. WeChat-SmallProgram:微信小程序中使用Async-await方法异步请求变为同步请求

    微信小程序中有些 Api 是异步的,无法直接进行同步处理.例如:wx.request.wx.showToast.wx.showLoading 等.如果需要同步处理,可以使用如下方法: 提示:Async ...

  3. angular中处理多个异步请求的方法汇总

    在实际业务中经常需要等待几个请求完成后再进行下一步操作.但angularjs中$http不支持同步的请求. 解决方法一: $http多层嵌套 $http.get('url1').success(fun ...

  4. vue中使用axios(异步请求)和mock.js 模拟虚假数据

    一.使用axios 1.安装 npm install --save axios 2.引用 import Axios from 'axios' Vue.prototype.Axios = Axios 二 ...

  5. vue(6)—— vue中向后端异步请求

    异步请求 其实什么是异步请求已经不用多说了,通俗的说,就是整个页面不会刷新,需要更新的部分数据做局部刷新,其他数据不变. 学到这里,你应该用过jquery里的ajax了,所以很能理解了,不多说了.详细 ...

  6. CAT中实现异步请求的调用链查看

    CAT简介 CAT(Central Application Tracking),是美团点评基于 Java 开发的一套开源的分布式实时监控系统.美团点评基础架构部希望在基础存储.高性能通信.大规模在线访 ...

  7. 实现在Android简单封装类似JQuery异步请求

    在android开发中经常会使用异步请求数据,通常会使用handler或者AsyncTask去做,handler 配合message 使用起来比较麻烦,AsyncTask 线程池只允许128个线程工作 ...

  8. Android简单封装类似JQuery异步请求

    在android开发中经常会使用异步请求数据,通常会使用handler或者AsyncTask去做,handler 配合message 使用起来比较麻烦,AsyncTask 线程池只允许128个线程工作 ...

  9. MVC&WebForm对照学习:ajax异步请求

    写在前面:由于工作需要,本人刚接触asp.net mvc,虽然webform的项目干过几个.但是也不是很精通.抛开asp.net webform和asp.net mvc的各自优劣和诸多差异先不说.我认 ...

随机推荐

  1. tLinux 2.2下安装Mono 4.8

    Tlinux2.2发行版基于CentOS 7.2.1511研发而成,内核版本与Tlinux2.0发行版保持完全一致,更加稳定,并保持对Tlinux2.0的完全兼容.Mono 4版本要求CentOS 7 ...

  2. XStream将java对象转换为xml时,对象字段中的下划线“_”,转换后变成了两个的解决办法

            在前几天的一个项目中,由于数据库字段的命名原因 其中有两项:一项叫做"市场价格"一项叫做"商店价格" 为了便于区分,遂分别将其命名为market ...

  3. C# i=0;i=i++,i的值是多少?

    昨天看群里dalao们聊天,有一个人出来问这个问题 这个题应该是挺常见的 int i = 0, t;        for(t = 0;t <= 5;t++)        {          ...

  4. PHP以接口方式实现多重继承(完全模拟)--学习笔记

     1.UML类图: 2.PHP代码: <?php /** * Created by PhpStorm. * User: andy * Date: 16-11-23 * Time: 下午7:57 ...

  5. 千呼万唤始出来,微软Power BI简体中文版官网终于上线了,中文文档也全了。。

    前几个月时间,研究微软Power BI技术,由于没有任何文档和资料,只能在英文官网瞎折腾,同时也发布了英文文档的相关文章:系列文章,刚好上周把文章发布完,结果简体中文版上线了.哈哈,心里有苦啊,早知道 ...

  6. wordpress优化之结合prism.js为编辑器自定义按钮转化代码

    原文链接 http://ymblog.net/2016/07/24/wordpress-prism/ 继昨天花了一天一夜的时间匆匆写了主题Jiameil3.0之后,心中一直在想着优化加速,体验更好,插 ...

  7. 【Java每日一题】20170104

    20170103问题解析请点击今日问题下方的"[Java每日一题]20170104"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...

  8. javascript arguments(转)

    什么是arguments arguments 是是JavaScript里的一个内置对象,它很古怪,也经常被人所忽视,但实际上是很重要的.所有主要的js函数库都利用了arguments对象.所以agru ...

  9. 查看mac中磁盘空间占用情况

    今天发现磁盘空间不够了,首先要找到那些文件夹占用了磁盘空间. du命令很好使 du -c -d 1 -m | sort -n -c 显示当前文件夹总计占用空间 -d 1 层级为1,即只显示当前目录下一 ...

  10. Atitit.项目修改补丁打包工具 使用说明

    Atitit.项目修改补丁打包工具 使用说明 1.1. 打包工具已经在群里面.打包工具.bat1 1.2. 使用方法:放在项目主目录下,执行即可1 1.3. 打包工具的原理以及要打包的项目列表1 1. ...