关于Curl在Swoole协程中的解决方案详析


Posted in PHP onSeptember 12, 2019

前言

众所周知,在 Swoole 应用中,是不推荐使用 Curl 的,因为 Curl 会阻塞进程。

本文会用实际的代码和数据,用最直观的方式,让你明白为什么。

最后还会给出 Curl 在 Swoole 中的解决方案,如果不想看分析可以直接拉到最后。

例程对比

宇润看文章不喜欢那些虚的,所以自己写也比较实在,直接来跑一下代码,用数据看为什么不推荐在 Swoole 使用 Curl。

为了偷懒,我直接用了 YurunHttp 的 Curl 和 Swoole Handler,来替代那些又臭又长的 Curl 代码。

代码
composer.json

{
  "require": {
    "yurunsoft/yurun-http": "~3.0"
  }
}

server.php

<?php
$http = new Swoole\Http\Server('127.0.0.1', 9501);
$http->on('workerstart', function(){
  \Swoole\Runtime::enableCoroutine();
});
$http->on('request', function ($request, $response) {
  sleep(1); // 假设各种处理耗时1秒
  $response->end($request->get['id'] . ': ' . date('Y-m-d H:i:s'));
});
$http->start();

test.php

<?php

use Yurun\Util\YurunHttp;
use Yurun\Util\HttpRequest;

require __DIR__ . '/vendor/autoload.php';

define('REQUEST_COUNT', 3);

go(function(){
  // 协程客户端
  echo 'coroutine http client:', PHP_EOL, PHP_EOL;
  $time = microtime(true);
  YurunHttp::setDefaultHandler(\Yurun\Util\YurunHttp\Handler\Swoole::class); // 切换为 Swoole Handler
  $channel = new \Swoole\Coroutine\Channel;
  for($i = 0; $i < REQUEST_COUNT; ++$i)
  {
    go(function() use($channel, $i){
      $http = new HttpRequest;
      $response = $http->get('http://127.0.0.1:9501/?id=' . $i); // 请求地址
      var_dump($response->body());
      $channel->push(1);
    });
  }
  for($i = 0; $i < REQUEST_COUNT; ++$i)
  {
    $channel->pop();
  }
  $channel->close();
  echo 'coroutine http client time: ', (microtime(true) - $time) . 's', PHP_EOL, PHP_EOL;

  // curl
  echo 'curl:', PHP_EOL, PHP_EOL;
  $time = microtime(true);
  YurunHttp::setDefaultHandler(\Yurun\Util\YurunHttp\Handler\Curl::class); // 切换为 Curl Handler
  $channel = new \Swoole\Coroutine\Channel;
  for($i = 0; $i < REQUEST_COUNT; ++$i)
  {
    go(function() use($channel, $i){
      $http = new HttpRequest;
      $response = $http->get('http://127.0.0.1:9501/?id=' . $i); // 请求地址
      var_dump($response->body());
      $channel->push(1);
    });
  }
  for($i = 0; $i < REQUEST_COUNT; ++$i)
  {
    $channel->pop();
  }
  $channel->close();
  echo 'curl time: ', (microtime(true) - $time) . 's', PHP_EOL, PHP_EOL;
});

运行

首次运行需要执行 composer update 安装依赖

运行 php server.php,启动服务端

运行 php test.php,启动客户端

运行结果

coroutine http client:

string(22) "1: 2019-09-11 08:35:54"
string(22) "0: 2019-09-11 08:35:54"
string(22) "2: 2019-09-11 08:35:54"
coroutine http client time: 1.0845630168915s

curl:

string(22) "0: 2019-09-11 08:35:55"
string(22) "1: 2019-09-11 08:35:56"
string(22) "2: 2019-09-11 08:35:57"
curl time: 3.0139901638031s

结果分析

上面的代码在服务端延迟 1 秒后返回结果,模拟实际业务的耗时。

通过客户端的耗时可以看出,Curl 3 次请求总共耗时 3 秒多,而协程客户端仅耗时 1 秒多。

因为前一次请求中,Curl 等待返回内容的时间是干不了其他事情的。而协程客户端等待返回内容期间,是挂起当前协程,转而再去执行其它协程中的代码。

解决方案

CoroutineHttpClient

使用 Swoole 内置的协程客户端实现,适合有一定基础的开发者使用。

文档:https://wiki.swoole.com/wiki/...

Guzzle-Swoole

我们在项目中,可能很少直接写 curl,但是用到的很多第三方类库(如某某云们的 SDK)会有用到。

这些第三方类库通常使用的是 Guzzle 作为 Http 客户端,而 Guzzle 底层也是使用 Curl 实现。

宇润专为此种场景研发了 Guzzle-Swoole 包,引入后可以让这些 SDK 轻松支持协程,而不用修改一行代码。

使用方法

执行命令直接安装依赖:composer require yurunsoft/guzzle-swoole ~1.1

全局设定处理器:

<?php
require dirname(__DIR__) . '/vendor/autoload.php';

use GuzzleHttp\Client;
use Yurun\Util\Swoole\Guzzle\SwooleHandler;
use GuzzleHttp\DefaultHandler;

DefaultHandler::setDefaultHandler(SwooleHandler::class);

go(function(){
  $client = new Client();
  $response = $client->request('GET', 'http://www.baidu.com', [
    'verify'  => false,
  ]);
  var_dump($response->getStatusCode());
});

手动指定 Swoole 处理器:

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Yurun\Util\Swoole\Guzzle\SwooleHandler;

go(function(){
  $handler = new SwooleHandler();
  $stack = HandlerStack::create($handler);
  $client = new Client(['handler' => $stack]);
  $response = $client->request('GET', 'http://www.baidu.com', [
    'verify'  => false,
  ]);
  var_dump($response->getBody()->__toString(), $response->getHeaders());
});

YurunHttp

YurunHttp 是开源的PHP HTTP类库,支持链式操作,简单易用。

支持所有常见的GET、POST、PUT、DELETE、UPDATE等请求方式,支持浏览器级别 Cookies 管理、上传下载、设置和读取header、Cookie、请求参数、失败重试、限速、代理、证书等。

3.0 版完美支持Curl、Swoole 协程;3.2 版支持 Swoole WebSocket 客户端。

使用方法

执行命令直接安装依赖:composer require yurunsoft/yurun-http ~3.2

<?php
use Yurun\Util\YurunHttp;
use Yurun\Util\HttpRequest;

// 设置默认请求处理器为 Swoole
YurunHttp::setDefaultHandler(\Yurun\Util\YurunHttp\Handler\Swoole::class);

// Swoole 处理器必须在协程中调用
go('test');

function test()
{
  $http = new HttpRequest;
  $response = $http->get('http://www.baidu.com');
  echo 'html:', PHP_EOL, $response->body();
}

截止发稿时,Swoole 4.4 新增的 hook Curl 依然是实验性功能。虽然宇润曾为该功能贡献过一部分代码,但是由于需要兼容的工作量非常大,有太多 OPTION 不被支持,我个人是暂时不推荐使用 hook Curl 的。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
简单介绍下 PHP5 中引入的 MYSQLI的用途
Mar 19 PHP
隐性调用php程序的方法
Mar 09 PHP
PHP得到某段时间区间的时间戳 php定时任务
Apr 12 PHP
有关PHP性能优化的介绍
Jun 20 PHP
AJAX的跨域访问-两种有效的解决方法介绍
Jun 22 PHP
PHP函数之日期时间函数date()使用详解
Sep 09 PHP
Codeigniter框架的更新事务(transaction)BUG及解决方法
Jul 25 PHP
php购物车实现方法
Jan 03 PHP
PHP代码实现表单数据验证类
Jul 28 PHP
在PHP站点的页面上添加Facebook评论插件的实例教程
Jan 08 PHP
php查找字符串中第一个非0的位置截取
Feb 27 PHP
PHP与JavaScript针对Cookie的读写、交互操作方法详解
Aug 07 PHP
PHP判断一个变量是否为整数、正整数的方法示例
Sep 11 #PHP
PHP中有关长整数的一些操作教程
Sep 11 #PHP
PHP生成随机字符串实例代码(字母+数字)
Sep 11 #PHP
Yii框架参数配置文件params用法实例分析
Sep 11 #PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
Sep 11 #PHP
Laravel框架验证码类用法实例分析
Sep 11 #PHP
PHP读取XML文件的方法实例总结【DOMDocument及simplexml方法】
Sep 10 #PHP
You might like
PHP 变量定义和变量替换的方法
2009/07/30 PHP
在Windows下编译适用于PHP 5.2.12及5.2.13的eAccelerator.dll(附下载)
2010/05/04 PHP
php基础学习之变量的使用
2011/06/09 PHP
mac下安装nginx和php
2013/11/04 PHP
CodeIgniter中使用cookie的三种方式详解
2014/07/18 PHP
PHP多线程编程之管道通信实例分析
2015/03/07 PHP
PHP不使用递归的无限级分类简单实例
2016/11/05 PHP
Array.prototype.slice 使用扩展
2010/06/09 Javascript
js类型检查实现代码
2010/10/29 Javascript
jquery 实现表单验证功能代码(简洁)
2012/07/03 Javascript
jquery实现简单的拖拽效果实例兼容所有主流浏览器(优化篇)
2013/06/28 Javascript
JavaScript全排列的六种算法 具体实现
2013/06/29 Javascript
改变隐藏的input中value的值代码
2013/12/30 Javascript
深入理解JavaScript系列(27):设计模式之建造者模式详解
2015/03/03 Javascript
jQuery对象与DOM对象之间的相互转换
2015/03/03 Javascript
Javascript中arguments用法实例分析
2015/06/13 Javascript
基于jquery实现页面滚动到底自动加载数据的功能
2015/12/19 Javascript
使用ionic在首页新闻中应用到的跑马灯效果的实现方法
2017/02/13 Javascript
BootstrapTable refresh 方法使用实例简单介绍
2017/02/20 Javascript
bootstrap table服务端实现分页效果
2017/08/10 Javascript
详解JS中统计函数执行次数与执行时间
2018/09/04 Javascript
JavaScript 判断iPhone X Series机型的方法
2019/01/28 Javascript
react高阶组件添加和删除props
2019/04/26 Javascript
vue.js实现数据库的JSON数据输出渲染到html页面功能示例
2019/08/03 Javascript
Vue-resource安装过程及使用方法解析
2020/07/21 Javascript
Python学习笔记之常用函数及说明
2014/05/23 Python
python2.x实现人民币转大写人民币
2018/06/20 Python
Python实现RabbitMQ6种消息模型的示例代码
2020/03/30 Python
Python同时迭代多个序列的方法
2020/07/28 Python
canvas进阶之贝塞尔公式推导与物体跟随复杂曲线的轨迹运动
2018/01/10 HTML / CSS
酒店执行总经理岗位职责
2013/12/15 职场文书
教师专业自荐书范文
2014/02/10 职场文书
邀请函范文
2015/02/02 职场文书
《悲惨世界》:比天空更广阔的是人的心灵
2020/01/16 职场文书
go语言基础 seek光标位置os包的使用
2021/05/09 Golang
python中的mysql数据库LIKE操作符详解
2021/07/01 MySQL