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

Laravel处理外部请求避免Worker闲置:Http::async实测遇阻

Laravel Job异步处理长耗时外部请求方案

Laravel原生完全支持你要的需求,核心思路是让发起请求的Job尽早结束以释放Worker,然后通过两种方式触发后续的ProcessResponse任务:

场景一:外部服务支持Webhook(最优方案)

如果外部服务支持回调通知,这是最高效的方式,不需要轮询浪费资源:

  1. 发起请求的Job只负责触发外部任务,带上你的Webhook回调地址后直接结束
  2. 外部服务处理完成后主动调用你的Webhook接口,接口再触发ProcessResponse任务

发起请求的Job代码

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;

class InitiateExternalRequest implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle()
    {
        // 发起外部请求,携带回调地址
        Http::post('https://external-service.com/long-task', [
            'callback_url' => route('webhooks.external-response'),
            // 其他业务参数
        ]);

        // 直接结束Job,Worker立刻被释放去处理其他任务
    }
}

Webhook接口实现

先在routes/api.php加路由:

Route::post('/webhooks/external-response', [WebhookController::class, 'handleExternalResponse'])->name('webhooks.external-response');

然后写控制器逻辑:

<?php

namespace App\Http\Controllers;

use App\Jobs\ProcessResponse;
use Illuminate\Http\Request;

class WebhookController extends Controller
{
    public function handleExternalResponse(Request $request)
    {
        // 务必添加Webhook签名验证,防止恶意请求
        // 比如根据外部服务提供的签名规则校验请求合法性

        // 触发响应处理Job
        ProcessResponse::dispatch($request->all());

        return response()->json(['status' => 'success']);
    }
}

场景二:外部服务不支持Webhook(轮询方案)

如果外部服务只能通过查询接口获取状态,就用任务释放+轮询的方式:

  1. 第一次执行Job时发起请求,拿到任务ID后释放Job(设置延迟时间)
  2. 延迟时间到后,Job再次执行,查询任务状态:未完成则继续释放,完成则触发ProcessResponse

轮询版Job代码

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;

class InitiateExternalRequest implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $taskId;

    // 构造函数接收任务ID(第一次执行时为null)
    public function __construct($taskId = null)
    {
        $this->taskId = $taskId;
    }

    public function handle()
    {
        if (!$this->taskId) {
            // 第一次执行:发起长耗时请求,获取任务ID
            $response = Http::post('https://external-service.com/long-task', [
                // 业务参数
            ]);

            $this->taskId = $response->json('task_id');

            // 释放任务,10秒后再次执行(根据实际情况调整延迟时间)
            $this->release(10);
            return;
        }

        // 后续执行:查询任务状态
        $statusRes = Http::get("https://external-service.com/tasks/{$this->taskId}/status");
        $status = $statusRes->json('status');

        if (in_array($status, ['pending', 'processing'])) {
            // 任务未完成,继续释放轮询
            $this->release(10);
            return;
        }

        if ($status === 'completed') {
            // 获取任务结果
            $result = Http::get("https://external-service.com/tasks/{$this->taskId}/result")->json();
            // 触发处理Job
            ProcessResponse::dispatch($result);
            // 任务完成,结束当前Job
            return;
        }

        // 任务失败,标记当前Job为失败
        $this->fail(new \Exception("外部任务执行失败,状态:{$status}"));
    }
}

为什么Http::async()达不到你的需求?

Http::async()是客户端层面的异步请求,本质上还是在当前Job进程中执行——如果不调用$promise->wait(),Job进程会直接结束,promise还没完成就被销毁,自然触发不了后续逻辑;如果调用wait(),进程会阻塞直到请求完成,Worker还是被占用,完全失去了异步释放的意义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:33:26