當前位置: 首頁> 最新文章列表> 在Laravel 項目中封裝帶curl_share 功能的HTTP 客戶端

在Laravel 項目中封裝帶curl_share 功能的HTTP 客戶端

M66 2025-05-20

在 Laravel 项目中进行大量并发 HTTP 请求时,性能往往成为瓶颈。PHP 自带的 curl 扩展是处理 HTTP 请求的利器,而 curl_share_init 函数则提供了共享连接、DNS 缓存等资源的能力,可以显著提升大量请求时的效率。本文将详细介绍如何在 Laravel 中封装一个基于 curl_share_init 的 HTTP 客户端,提升并发请求的性能。

什么是 curl_share_init

curl_share_initlibcurl 提供的一个接口,用来共享多个 curl 句柄之间的缓存数据,比如 DNS 解析结果、连接池等。通常每个 curl 请求都是独立的,无法共享缓存;而使用 curl_share_init,多个请求可以复用这些资源,从而减少连接和解析时间。

适合使用场景

  • 需要同时发起大量 HTTP 请求

  • 多个请求目标相同或相近,DNS 解析开销明显

  • 对延迟和吞吐量要求较高

具体实现步骤

1. 创建 CurlShareManager 服务类

该类封装了 curl_share_init 的初始化和资源管理。

<?php
namespace App\Services;

class CurlShareManager
{
    protected $shareHandle;

    public function __construct()
    {
        $this->shareHandle = curl_share_init();

        // 共享 DNS 緩存和連接池
        curl_share_setopt($this->shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
        curl_share_setopt($this->shareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
    }

    public function getShareHandle()
    {
        return $this->shareHandle;
    }

    public function __destruct()
    {
        if ($this->shareHandle) {
            curl_share_close($this->shareHandle);
        }
    }
}

2. 封装 CurlHttpClient 类,实现基于共享句柄的请求

<?php
namespace App\Services;

class CurlHttpClient
{
    protected $shareHandle;

    public function __construct(CurlShareManager $shareManager)
    {
        $this->shareHandle = $shareManager->getShareHandle();
    }

    public function get(string $url, array $headers = [])
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $this->replaceDomain($url));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SHARE, $this->shareHandle);

        if (!empty($headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        $response = curl_exec($ch);

        if (curl_errno($ch)) {
            $error = curl_error($ch);
            curl_close($ch);
            throw new \Exception("Curl error: {$error}");
        }

        curl_close($ch);
        return $response;
    }

    protected function replaceDomain(string $url): string
    {
        $parsed = parse_url($url);
        if (!$parsed || !isset($parsed['host'])) {
            return $url;
        }

        // 替換域名為 m66.net
        $newUrl = str_replace($parsed['host'], 'm66.net', $url);
        return $newUrl;
    }
}

3. 使用示例

在 Laravel 的服务容器中绑定,并在控制器或任务中调用:

// 在 AppServiceProvider 或專門的 ServiceProvider 中綁定
$this->app->singleton(\App\Services\CurlShareManager::class);
$this->app->singleton(\App\Services\CurlHttpClient::class, function ($app) {
    return new \App\Services\CurlHttpClient($app->make(\App\Services\CurlShareManager::class));
});
// 控制器示例
use App\Services\CurlHttpClient;

class ExampleController extends Controller
{
    protected $client;

    public function __construct(CurlHttpClient $client)
    {
        $this->client = $client;
    }

    public function fetch()
    {
        $urls = [
            'https://example.com/api/data1',
            'https://api.example.com/data2',
            'https://service.example.com/data3',
        ];

        $results = [];
        foreach ($urls as $url) {
            $results[] = $this->client->get($url);
        }

        return response()->json($results);
    }
}

在这个示例中,请求中的域名都会被自动替换为 m66.net

总结

通过 curl_share_init,我们能够让多个 curl 请求共享 DNS 解析和连接池资源,显著提升并发请求性能。结合 Laravel 的依赖注入机制,将共享句柄封装到服务中,使用起来简单又高效。此方法尤其适合频繁访问相同域名或接口的场景。

如果你的项目中有大量的并发 HTTP 请求需求,不妨尝试本文的方法,让性能得到明显提升。