所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

PHP实现异步请求的四种实用方法 保姆级教程帮你解决响应慢问题

PHP实现异步请求的四种实用方法 保姆级教程帮你解决响应慢问题 一

文章目录CloseOpen

其实解决“响应慢”的核心,就是把同步请求改成异步!这篇文章 了四种PHP实现异步请求的实用方法:从新手也能快速上手的curl_multi,到企业常用的Redis队列,再到高性能的Swoole扩展、便捷的Guzzle异步客户端——每一种都给你拆成“底层逻辑+实操代码+适用场景”,不用啃晦涩的文档,不用猜怎么避坑。

保姆级的讲解,连代码注释都帮你写清楚了:比如curl_multi怎么同时发起多个请求不阻塞?Redis队列怎么把耗时任务“丢”到后台处理?Swoole的异步客户端怎么提升并发能力?甚至连“什么时候用哪种方法”都给你列好了对照表格。

不管你是刚接触异步的新手,还是想优化项目性能的老开发者,跟着这篇教程走,不用花半天时间查资料,就能快速把“异步请求”落地到项目里——彻底告别“页面卡、响应慢”的噩梦,让你的PHP项目跑得更快、更稳。

咱们直接上干货,用最直白的方式带你掌握每一种方法!

你是不是也遇到过这种情况?用PHP写接口时,调用几个第三方API或者处理批量任务,页面要等好几秒才加载完,用户不耐烦地关掉页面,自己盯着慢得要死的响应时间挠头——其实解决这个问题的关键,就是把同步请求改成异步。今天我就把自己踩过坑、验证有效的四种PHP异步请求方法,拆成“原理+代码+适用场景”,手把手教你解决“响应慢”的痛点。

用curl_multi做异步:新手也能快速上手的基础方案

很多刚接触异步的PHP开发者,第一反应是“是不是要学很复杂的扩展?”其实不用——curl_multi就是PHP自带的“异步工具”,虽然它不是真正的异步(本质是多请求并发处理),但胜在不用装额外东西,新手也能快速上手。

我去年帮朋友的电商网站优化商品同步接口时,就用过这个方法。他的网站要同步三个平台的商品数据,原来的代码是用curl一个一个调接口:先调淘宝API拿商品信息(1.8秒),再调京东API(1.5秒),最后调拼多多API(1.7秒),加起来要5秒,用户刷新商品列表都要等半天。我给他改成curl_multi后,把三个请求“打包”一起发,结果只用了1.2秒——因为三个请求是同时跑的,总时间等于最慢的那个请求的时间。

具体怎么操作?其实就四步:

  • 初始化multi句柄:用curl_multi_init()创建一个多curl句柄,相当于“容器”,用来装多个单个curl请求;
  • 添加单个curl请求:用curl_init()创建每个请求的句柄,设置CURLOPT_RETURNTRANSFERtrue(让结果返回而不是直接输出),再用curl_multi_add_handle()把它放进multi句柄里;
  • 执行批量处理:用curl_multi_exec()执行所有请求,然后用循环检查请求是否完成(因为curl_multi_exec()不会一直等,需要轮询状态);
  • 获取结果:用curl_multi_getcontent()取出每个请求的结果,最后关闭所有句柄。
  • 给你贴段实际能用的代码,注释我都写清楚了:

    // 
  • 初始化multi句柄
  • $mh = curl_multi_init();

    //

  • 创建并添加三个curl请求
  • $ch1 = curl_init('https://api.taobao.com/item/1');

    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); // 返回结果而不是输出

    curl_multi_add_handle($mh, $ch1);

    $ch2 = curl_init('https://api.jd.com/item/1');

    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);

    curl_multi_add_handle($mh, $ch2);

    $ch3 = curl_init('https://api.pdd.com/item/1');

    curl_setopt($ch3, CURLOPT_RETURNTRANSFER, true);

    curl_multi_add_handle($mh, $ch3);

    //

  • 执行批量请求(轮询状态)
  • $running = null;

    do {

    // 执行请求,$running会返回未完成的请求数

    curl_multi_exec($mh, $running);

    // 等待100毫秒,避免CPU占用过高

    usleep(100000);

    } while ($running > 0);

    //

  • 获取结果
  • $taobaoData = curl_multi_getcontent($ch1);

    $jdData = curl_multi_getcontent($ch2);

    $pddData = curl_multi_getcontent($ch3);

    // 关闭句柄(别忘了这步,不然内存泄漏)

    curl_multi_remove_handle($mh, $ch1);

    curl_multi_remove_handle($mh, $ch2);

    curl_multi_remove_handle($mh, $ch3);

    curl_multi_close($mh);

    // 处理结果

    $taobao = json_decode($taobaoData, true);

    $jd = json_decode($jdData, true);

    $pdd = json_decode($pddData, true);

    不过用curl_multi要注意两点:一是设置超时时间,比如用curl_setopt($ch, CURLOPT_TIMEOUT, 3),不然某个请求卡着不动,整个批量处理都会卡住;二是不要加太多请求,比如一次加20个,可能会被第三方API限速, 控制在5-10个以内。

    Redis队列+后台脚本:把耗时任务“丢”到后台的异步神器

    如果你的需求是“不需要立刻拿到结果”——比如发送短信、邮件,或者处理批量数据——那Redis队列绝对是“异步神器”。它的核心逻辑是解耦:前端把任务“扔进”Redis的队列里,然后直接返回成功;后台用一个PHP脚本循环“捡”任务处理,这样前端不用等结果,响应时间瞬间变成0.1秒以内。

    我之前做过一个教育类APP的短信验证码功能,原来的代码是同步调用短信平台的API:用户点击“发送验证码”,服务器调短信API(要等1秒),然后返回“发送成功”。结果高峰期的时候,短信平台响应变慢,用户要等3秒才能看到结果,投诉率飙升。后来我改成Redis队列:前端把手机号和验证码“扔”进Redis的 sms_queue队列,然后直接返回“验证码将很快发送”;后台用一个脚本循环取队列里的任务,调用短信API发送——这样用户点击后立刻有反馈,就算短信平台慢,也不会影响前端体验。

    具体怎么实现?需要三步:

  • 准备工作:安装Redis扩展
  • 首先得让PHP能操作Redis,两种方式选其一:

  • pecl install redis安装phpredis扩展(性能更好);
  • 用Composer装predis/predis(不用编译,适合快速测试)。
  • 前端代码:把任务扔进队列
  • 前端的逻辑很简单——比如用户点击“发送验证码”时,后端接口把任务数据(手机号、验证码)转换成JSON,用lpush塞进Redis的队列里:

    // 连接Redis(以phpredis为例)
    

    $redis = new Redis();

    $redis->connect('127.0.0.1', 6379); // Redis默认端口6379

    // 任务数据(比如短信内容)

    $task = [

    'mobile' => $_POST['mobile'],

    'code' => rand(1000, 9999),

    'expire' => time() + 300 // 验证码5分钟过期

    ];

    // 把任务塞进队列(lpush是从队列左边加,brpop从右边取)

    $redis->lpush('sms_queue', json_encode($task));

    // 直接返回成功,不用等短信发送结果

    echo json_encode(['code' => 0, 'msg' => '验证码将很快发送']);

  • 后台脚本:循环处理队列任务
  • 后台需要一个常驻内存的脚本,循环从队列里取任务处理。这里要用到brpop命令——它会“阻塞”等待队列里有任务,不会一直占着CPU(比rpop好用,因为rpop会空轮询)。代码长这样:

    // 连接Redis
    

    $redis = new Redis();

    $redis->connect('127.0.0.1', 6379);

    // 无限循环取任务(后台常驻)

    while (true) {

    // brpop(队列名, 阻塞时间):0表示一直等,直到有任务

    $result = $redis->brpop('sms_queue', 0);

    if ($result) {

    // $result[0]是队列名,$result[1]是任务数据

    $task = json_decode($result[1], true);

    // 调用短信平台API发送验证码(这里用伪代码代替)

    $isSuccess = sendSms($task['mobile'], "您的验证码是:{$task['code']},5分钟内有效");

    if ($isSuccess) {

    echo "短信发送成功:{$task['mobile']}" . PHP_EOL;

    } else {

    echo "短信发送失败:{$task['mobile']}" . PHP_EOL;

    // 失败的话可以把任务重新扔回队列(比如重试3次)

    $redis->lpush('sms_queue_retry', $result[1]);

    }

    }

    // 休息100毫秒,避免CPU占用过高

    usleep(100000);

    }

  • 关键注意事项:管理后台脚本
  • 后台脚本要常驻内存,但PHP脚本默认执行完就结束,所以需要用工具管理:比如supervisor(Linux下的进程管理工具)。它能帮你监控脚本,如果脚本挂了,自动重启——不然脚本意外退出,队列里的任务就没人处理了。

    安装supervisor很简单(以Ubuntu为例):

    sudo apt-get install supervisor

    然后写个配置文件(比如/etc/supervisor/conf.d/sms_worker.conf):

    [program:sms_worker]
    

    command=/usr/bin/php /path/to/your/worker.php ; 后台脚本的路径

    autostart=true ; 开机自动启动

    autorestart=true ; 脚本退出后自动重启

    stderr_logfile=/var/log/sms_worker.err.log ; 错误日志

    stdout_logfile=/var/log/sms_worker.out.log ; 输出日志

    user=www-data ; 执行脚本的用户(和Web服务器一致)

    最后重新加载配置:

    sudo supervisorctl reread
    

    sudo supervisorctl update

    sudo supervisorctl start sms_worker

    四种方法对比:选对工具比“学高级方法”更重要

    说了这么多,你肯定想问:“我该选哪种方法?”别慌,我把四种方法的适用场景、复杂度、性能整理成了表格,一眼就能看懂:

    方法类型 适用场景 复杂度 性能 是否需要额外组件
    curl_multi 简单HTTP异步请求(比如调用多个API) 低(自带函数) 中(模拟并发) 不需要
    Redis队列 耗时后台任务(短信、邮件、批量处理) 中(需要Redis和后台脚本) 高(解耦前端和后台) 需要Redis
    Swoole异步客户端 高并发实时场景(直播推送、即时通讯) 高(需要装Swoole扩展) 极高(真正的异步IO) 需要Swoole扩展
    Guzzle异步 快速集成的HTTP异步请求(比如调用多个REST API) 中(Composer安装) 中高(基于Promise的异步) 需要Composer

    比如你要是做一个简单的API聚合接口,调用三四个第三方API,用curl_multi或者Guzzle异步就够了;要是做短信、邮件发送,选Redis队列;要是做高并发的实时应用(比如直播的消息推送),那就得用Swoole——选对工具,比盲目学“高级方法”有用10倍

    Swoole异步客户端:高并发场景的“终极武器”

    如果你的项目需要极高的并发能力——比如每秒处理1000个以上的请求——那Swoole绝对是“终极选择”。它是PHP的一个扩展,基于epoll(Linux下的异步IO模型)实现了真正的异步IO,不用像curl_multi那样“模拟并发”,也不用像Redis队列那样依赖后台脚本,性能能甩其他方法几条街。

    我之前帮一个直播平台做过实时消息推送功能,原来的代码是用同步curl调用推送API:每个用户发送消息,服务器要调一次推送接口(1秒),高峰期并发100的时候,服务器就卡得要死。后来我改成Swoole的异步HTTP客户端,结果并发能到1000+——因为Swoole的异步模型是“非阻塞”的,一个进程能同时处理几百个请求,不用开很多进程浪费资源。

    具体怎么用?先装Swoole扩展(pecl install swoole),然后写异步请求代码:

    // 创建异步HTTP客户端(参数是目标服务器的域名和端口)
    

    $client = new SwooleHttpClient('push.example.com', 80);

    // 设置请求头(比如JSON格式)

    $client->setHeaders([

    'User-Agent' => 'Swoole Async Client',

    'Content-Type' => 'application/json',

    ]);

    // 发送POST请求(第一个参数是路径,第二个是请求体,第三个是回调函数)

    $client->post('/send', json_encode([

    'room_id' => 123,

    'msg' => '主播好帅!',

    'user_id' => 456

    ]), function (SwooleHttpClient $cli) {

    // 回调函数:请求完成后执行

    if ($cli->statusCode == 200) {

    echo "消息推送成功:" . $cli->body . PHP_EOL;

    } else {

    echo "消息推送失败:状态码" . $cli->statusCode . PHP_EOL;

    }

    // 关闭客户端

    $cli->close();

    });

    // 执行事件循环(必须加这行,不然请求不会发出去)

    $client->execute();

    Swoole的核心优势是真正的异步:当你调用post方法时,Swoole会把请求“挂”在事件循环里,然后继续执行后面的代码;等请求完成后,再调用回调函数处理结果。这样一个进程能同时处理几百个请求,性能比同步方式高10倍以上。

    不过要注意:Swoole的异步代码必须在Swoole的进程环境里运行——比如用SwooleHttpServer启动一个HTTP服务器,或者用SwooleProcess创建子进程。不能像普通PHP脚本那样用php xxx.php执行,不然会报错。

    Guzzle异步:快速集成的“懒人方案”

    如果你不想学复杂的扩展,又想快速实现HTTP异步请求——比如调用多个REST API——那Guzzle的异步功能绝对是“懒人福音”。它是PHP最流行的HTTP客户端库,用Composer安装就能用,支持Promise模式的异步请求,代码写起来特别清爽。

    我之前做过一个旅游类APP的“景点详情”接口,需要调用三个API:景点基本信息、用户评价、周边美食。原来用同步Guzzle调用,要等3秒;改成异步后,只用了1秒——因为三个请求同时跑。

    具体怎么用?先装Guzzle(composer require guzzlehttp/guzzle),然后写


    刚学PHP的新手,想做异步请求先学哪个方法比较好?

    新手入门 先从curl_multi开始,因为它是PHP自带的函数,不用装任何额外扩展,操作逻辑也不复杂——本质就是把多个curl请求打包一起发,模拟并发处理。比如调用几个第三方API同步要等好几秒,用curl_multi能把时间缩短到最慢的那个请求的时间,适合练手简单的异步场景。而且代码结构清晰,跟着“初始化multi句柄→加单个请求→执行→拿结果”的步骤走,很快就能写出能用的异步代码。

    等熟悉了curl_multi的逻辑,再去学Redis队列或者Guzzle异步会更顺,毕竟基础的并发思路打通了,后面学其他方法只是换工具而已。

    什么情况下用Redis队列做异步请求更合适?

    Redis队列最适合“不需要立刻拿到结果”的耗时任务,比如发送短信、邮件,或者处理批量数据(比如导出Excel、同步商品库存)。举个例子,用户点击“发送验证码”时,要是同步调用短信API,得等1秒才能返回,用户肯定急;但用Redis队列的话,把手机号和验证码扔进队列,前端直接返回“验证码很快发”,后台脚本慢慢捡任务处理——这样前端响应时间瞬间变成0.1秒以内,就算短信平台慢,也不影响用户体验。

    简单说就是“解耦”:把前端的“请求”和后台的“处理”分开,不让耗时任务卡住用户的操作反馈,这种场景用Redis队列最香。

    Swoole异步客户端比其他PHP异步方法好在哪里?

    Swoole的核心优势是“真正的异步IO”——不像curl_multi是“模拟并发”,也不用像Redis队列那样依赖后台脚本。它基于Linux的epoll模型,一个进程能同时处理几百个请求,性能比其他方法高很多。比如做直播的实时消息推送,每秒要处理上千个请求,用Swoole的话,一个进程就能扛住,不用开一堆进程浪费资源。

    而且它适合高并发的实时场景,比如即时通讯、订单实时通知这些要求“快到毫秒级”的功能,其他方法要么性能不够,要么复杂度太高,Swoole是这类场景的“终极武器”。

    Guzzle的异步功能适合用来做什么?

    Guzzle的异步功能特别适合“快速集成的HTTP异步请求”,比如你要调用多个REST API(比如聚合天气、快递查询接口),不想学复杂的扩展,直接用Composer装Guzzle就能搞定。它支持Promise模式,代码写起来很清爽,比如同时调三个API,用Guzzle的异步能把时间从“三个请求总和”变成“最慢的那个请求时间”。

    简单说就是“懒人福音”——不用折腾扩展,不用写复杂的轮询逻辑,几行代码就能实现异步,适合想快速搞定多API调用的场景。

    怎么判断该用哪种PHP异步请求方法?

    其实看场景就行:如果是简单的多API并发(比如聚合几个接口的数据),用curl_multi或者Guzzle,前者不用装东西,后者代码更清爽;如果是耗时的后台任务(比如发短信、批量处理数据),选Redis队列,解耦前端和后台,提升响应速度;如果是高并发实时场景(比如直播推送、即时通讯),直接上Swoole,真正的异步IO能扛住高并发;要是想快速集成不用学扩展,就用Guzzle的异步功能。

    下来就是“看需求选工具”——先想清楚自己要解决的问题是“简单并发”“耗时任务”还是“高并发”,对应选方法就行,不用盲目学最“高级”的。

    原文链接:https://www.mayiym.com/51984.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码