PHP模拟并发请求的两种实现方式:HTTP连接与cURL_multi

陆景行
陆景行
发布时间:2026-10-14| 2人浏览过
原创

PHP原生环境通常无法直接创建操作系统级的线程,但在Web服务器端,开发者常需实现类似并发的功能以提升效率。本文将解析两种常用的“伪多线程”或并发技术方案:一是基于fsockopen建立独立HTTP连接实现的自定义Thread类,二是利用PHP扩展cURL的multi句柄进行并行数据获取的方法,帮助解决日志写入阻塞或批量图片/数据下载的性能瓶颈问题。

通过fsockopen模拟并发请求

在需要执行耗时任务但不想阻塞当前页面响应的场景下(如后台日志记录),可以利用PHP的 fsockopen 函数向自身脚本发起新的GET请求。这种方式本质上是开启了多个独立的Web请求进程,它们并行执行并随后关闭,从而达到了近似多线程的效果。

第1步:定义并发线程管理类

创建一个名为 thread 的类,用于存储待执行的函数名及其参数,并在初始化时启动并发流程。核心逻辑分为两步:

  1. 添加任务:使用 addthread() 方法将回调函数和对应参数压入队列。
  2. 执行任务:在 runthread() 中,若无特定 flag 参数,则循环遍历所有任务,利用 fsockopen 向当前域名和端口发送一个带有索引标识的GET请求;若检测到 flag,则只执行对应索引的函数。
class thread {
    var $hooks = array();
    var $args = array();

    function thread() {}

    function addthread($func) {
        $args = array_slice(func_get_args(), 1);
        $this->hooks[] = $func;
        $this->args[] = $args;
        return true;
    }

    function runthread() {
        // 检查是否指定了要执行的具体线程索引
        if(isset($_GET['flag'])) {
            $flag = intval($_GET['flag']);
        }
        
        if($flag !== null && ($flag || $flag === 0)) {
            // 执行指定索引的回调函数
            call_user_func_array($this->hooks[$flag], $this->args[$flag]);
        } else {
            // 并发发起所有请求
            for($i = 0, $size = count($this->hooks); $i < $size; $i++) {
                $fp = fsockopen($_SERVER['HTTP_HOST'], $_SERVER['SERVER_PORT']);
                if($fp) {
                    // 构造HTTP请求,带上flag参数指向当前循环索引
                    $out = "GET {$_SERVER['PHP_SELF']}?flag=$i HTTP/1.1\r\n";
                    $out .= "Host: {$_SERVER['HTTP_HOST']}\r\n";
                    $out .= "Connection: Close\r\n\r\n";
                    fputs($fp, $out);
                    fclose($fp);
                }
            }
        }
    }
}

第2步:编写业务回调函数并调用

定义具体的业务逻辑函数(如记录日志),并通过实例化 thread 类添加任务。注意,这些函数将被新的HTTP进程执行,因此应尽量避免依赖全局状态或复杂的上下文切换。

$thread = new thread();
$thread->addthread('action_log', 'a');
$thread->addthread('action_log', 'b');
$thread->addthread('action_log', 'c');
$thread->runthread();

function action_log($info) {
    $log = 'log/' . microtime() . '.log';
    $txt = $info . "\r\n" . 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "\r\n";
    $fp = fopen($log, 'w');
    fwrite($fp, $txt);
    fclose($fp);
}

提示:上述代码展示了基本的并发写入逻辑。实际生产环境中,考虑到文件IO竞争,建议对 fopen 操作做好互斥锁处理或使用队列服务。

利用cURL_multi实现高效并发抓取

当任务涉及网络请求且需要获取返回结果时(如批量下载图片或API数据),直接使用上面的HTTP连接方式难以收集返回值。此时,PHP内置的 cURL 扩展提供了强大的多句柄处理机制 curl_multi_* 系列函数,这是实现真正意义上高效并发的标准方案。

第1步:封装并发执行类

该类主要包含以下配置与方法:

  • 配置项:包括超时时间、是否跟随重定向、是否返回头部等常用选项。
  • setUrlList():设置待抓取的URL列表数组。
  • execute():核心的并发执行逻辑。它初始化 curl_multi_init,将每个URL创建的 curl_init 句柄加入multi管理器,然后进入循环状态等待所有请求完成。
class curl_multi{
    private $url_list=array();
    private $curl_setopt=array(
        'CURLOPT_RETURNTRANSFER' => 1,
        'CURLOPT_HEADER' => 0,
        'CURLOPT_NOBODY' => 0,
        'CURLOPT_FOLLOWLOCATION' => 0,
        'CURLOPT_TIMEOUT' => 6
    );

    function __construct($seconds=30){
        set_time_limit($seconds);
    }

    public function setUrlList($list=array(){
        $this->url_list=$list;
    }

    public function execute(){
        $mh=curl_multi_init();
        foreach($this->url_list as $i=>$url){
            $conn[$i]=curl_init($url);
            foreach($this->curl_setopt as $key => $val){
                curl_setopt($conn[$i],preg_replace('/(CURLOPT_w{1,})/ie','$0',$key),$val);
            }
            curl_multi_add_handle($mh,$conn[$i]);
        }
        $active=false;
        do{
            $mrc=curl_multi_exec($mh,$active);
        }while($mrc == CURLM_CALL_MULTI_PERFORM);

        while($active and $mrc == CURLM_OK){
            if(curl_multi_select($mh) != -1){
                do{
                    $mrc=curl_multi_exec($mh,$active);
                }while($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        $res=array();
        foreach($this->url_list as $i => $url){
            $res[$i]=curl_multi_getcontent($conn[$i]);
            curl_close($conn[$i]);
            curl_multi_remove_handle($mh,$conn[$i]);
        }
        curl_multi_close($mh);
        return $res;
    }
}

第2步:执行批量任务并保存结果

实例化后,传入一组图片URL,执行并发下载并将二进制内容保存到本地文件。此方法显著优于串行请求(如 file_get_contents 循环)。

$curl_mul=new curl_multi();
// 这里演示下载同一张图多次以展示并发能力
$url_list = array(
    'https://www.jb51.net/img/logo.jpg', 
    'https://www.jb51.net/img/logo.jpg', 
    'https://www.jb51.net/img/logo.jpg'
);
$curl_mul->setUrlList($url_list);
$result_data=$curl_mul->execute();

$i=1;
foreach($result_data as $v){
    $filename=$i.'.gif';
    $fp2=@fopen($filename,'a');
    fwrite($fp2,$v);
    fclose($fp2);
    $i++;
}

需要注意的是,在使用 curl_multi 时,务必确保服务器已加载 PHP cURL 扩展,并且目标主机允许高频并发连接,以免触发防刷机制或被服务器拒绝。以上就是PHP实现类似多线程并发处理的详细内容,更多关于PHP并发编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。