You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP是否支持多线程?Apache环境下请求阻塞问题咨询

问题描述

基于React开发的前端向API网关发起请求,网关将请求转发至相关服务(另一个API调用)。用户1点击按钮B,请求由API网关的MyController控制器的myMethod方法处理;随后用户2点击同一按钮B,请求同样由该控制器的同一方法处理(已通过打开两个独立浏览器测试)。

用户2在用户1点击后立即操作,发现若用户1的请求执行耗时15秒,用户2的请求会被阻塞,需等待用户1的请求完成后才会执行。这与预期不符,仿佛两个请求由同一线程处理导致阻塞。

更准确地说,网关中执行阻塞逻辑的方法会向某API发起异步HTTP请求,原以为用户2的请求会在不同线程中运行,能在用户1请求完成前返回结果。

为测试该阻塞问题,为用户1设置了一个带延迟的API接口(使用sleep()):

Route::middleware('auth:sanctum')->get('testCallSlow', function (){
    sleep(15);
    return json_encode('some result for user 1');
});

为用户2设置了另一个接口:

Route::middleware('auth:sanctum')->get('testCall', function (){  
    return json_encode('some result for user 2');
});

请问这是正常行为吗?部署到生产服务器后情况会不同吗?
我想了解生产服务器的线程处理是否会有差异,在考虑使用RabbitMQ前先咨询是否有其他人遇到过相同问题。

我的开发环境为XAMPP+Apache,通过虚拟主机部署,未使用php artisan serve(最初使用过,曾认为本地开发服务器可能是单线程简化版本导致问题)。

前端调用API网关的方法如下(两个用户均调用此方法):

public function userDetails(Request $request)
{
    // check user has no permission to proceed, if not return 403
    if (!$this->permissionService->userCanAccess(auth()->user(), Microservice::RESCOM_MSERVICE_ID))
    {
        return response()->json(['message' => 'user does not have permission to access the requested resource.'])->setStatusCode(403);
    }
    $rescom_token = $this->getSessionToken(); // writes token to session the 1st time, any subsequent calls only read.

    // proceed with call to rescom api
    $url = 'http://rescom.io/api/testCall';

    // This -if is only put here for testing two different users, 
    // each user's request is forwarded to different endpoints (see 
    // actual end points above)
    if (auth()->user()->id == 2)
    {
        $url = 'http://rescom.io/api/testCallSlow';
    }

    try
    {
        $body = '';
        $promise = Http::withToken($rescom_token)->async()->get($url)->then(function($response) use (& $body)
        {
            $body = $response->body();
        });

        $promise->wait();

        return $body;
    }
    catch (\Exception $e)
    {
        return \response()->json($e->getMessage());
    }
}
问题解答
  • 当前阻塞是否正常?
    这种阻塞是不正常的,核心问题出在代码里的$promise->wait()调用——尽管你用async()创建了异步HTTP请求,但wait()会强制当前进程阻塞等待请求返回,直到完成才会继续执行后续代码。另外,XAMPP默认的Apache可能使用mpm_prefork多进程模型,但如果配置的进程数过少,所有进程都被阻塞在wait()时,新请求就会排队,看起来像是单线程处理。

  • 生产环境会有差异吗?
    如果生产环境的Apache/Nginx+PHP-FPM配置合理(比如设置了足够的工作进程/线程数),阻塞情况会有所缓解,但无法彻底解决。只要代码中存在wait()同步等待,每个请求都会占用一个工作进程/线程直到外部API返回,高并发下大量这类请求仍会导致排队阻塞。

  • 解决方案

    • 移除wait(),改用异步队列处理:如果业务允许不实时返回结果,将请求逻辑放入Laravel Queue,前端先收到“请求已受理”的响应,后续通过轮询或WebSocket获取最终结果。
    • 使用非阻塞HTTP客户端:若必须实时返回结果,可考虑ReactPHP等异步框架,但需要重构现有代码,成本较高。
    • 调整服务器配置:增加Apache/PHP-FPM的工作进程数,提升并发处理能力,但这只是治标不治本,无法应对大量阻塞请求的场景。
  • RabbitMQ是否必要?
    如果业务场景支持异步处理(不需要实时返回结果),RabbitMQ这类消息队列是理想方案——将请求任务放入队列,由消费者进程异步处理,网关可立即响应前端,避免阻塞。但如果必须实时返回结果,消息队列无法解决问题,需从HTTP请求的异步处理逻辑入手。

内容的提问来源于stack exchange,提问作者ElQuico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:40:25