各路 PHP 高手,谁用过 PHP 的并发?是指 composer 里的工具包 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
abccccabc
V2EX    PHP

各路 PHP 高手,谁用过 PHP 的并发?是指 composer 里的工具包

  •  
  •   abccccabc 2024 年 3 月 16 日 3733 次点击
    这是一个创建于 695 天前的主题,其中的信息可能已经有所发展或是发生改变。
    各路 PHP 高手,谁用过 PHP 的并发?主要是 composer 里的工具包,并不是指 swoole 。

    目的:用于获取 web 图片
    使用场景:复制一篇经典的文章到我自己的论坛,可能含有图片 20 张左右,如果使用 curl 一张一张的取,网络不好的时候可能会超时,如果图片更多,绝对超时;如果用并发 curl 取图片,应该会很快。

    希望此 composer 包能兼容的 php 版本多些。
    37 条回复    2024-04-18 11:24:18 +08:00
    shiny
        1
    shiny  
    PRO
       2024 年 3 月 16 日
    不试试 curl_multi_exec ?
    a282810
        2
    a282810  
       2024 年 3 月 16 日
    simonlu9
        3
    simonlu9  
       2024 年 3 月 16 日   1
    pthread 试下
    huyiwei
        4
    huyiwei  
       2024 年 3 月 16 日
    多开几个进程呗
    a282810
        5
    a282810  
       2024 年 3 月 16 日
    wonderfulcxm
        6
    wonderfulcxm  
       2024 年 3 月 16 日 via iPhone
    guzzle 就可以
    abccccabc
        7
    abccccabc  
    OP
       2024 年 3 月 16 日
    ```
    @a282810
    以前爬东西用过 https://github.com/ares333/php-curl
    ```
    简单看了一下使用示例,并没有看到并发的相关操作。而且此包很久都没有更新了。处于无维护状态。
    panlatent
        8
    panlatent  
       2024 年 3 月 16 日
    用 curl 拓展或者 Guzzle 库,推荐后者, 尽量不要尝试多进场/线程方案,坑太多。
    k9982874
        9
    k9982874  
       2024 年 3 月 16 日 via Android
    php 现在能并发了?牛!另外爬东西不试试 py ?
    vovov
        10
    vovov  
       2024 年 3 月 16 日 via iPhone
    我选择使用队列
    hefish
        11
    hefish  
       2024 年 3 月 16 日
    这思路不对啊,这不是应该往异步方面去考虑吗?怎么硬往并发上去凑?
    a282810
        12
    a282810  
       2024 年 3 月 16 日
    @abccccabc #7 其实是基于 curl_multi_exec , 能实现需要的功能就可以了,Curl::add()添加任务到任务池,Curl::start()开始执行任务并阻塞
    abccccabc
        13
    abccccabc  
    OP
       2024 年 3 月 16 日
    @k9982874 不是做爬虫,是复制 web 文章到我的论坛,同时要将图片进行本地化。而且还要替换掉内容中的外部图片地址。

    @a282810 https://github.com/ares333/php-curl 这个项目里感觉更像异步,并发的话,肯定还要处理后续事情。他的项目示例里没有体现出来。


    我是复制一些网络上优秀的文章到 dz 论坛,同时需要替换掉文章内容对应的 bbcode 图片代码。异步真不知道怎么操作,并发的话,我知道后续会有一个结果,对这个结果 foreach 就可以得到下载后图片。方便处理后续替换入库等操作。
    abccccabc
        14
    abccccabc  
    OP
       2024 年 3 月 16 日
    @a282810 要求 php7.1 以上的 https://github.com/Yurunsoft/YurunHttp

    这个工具包可以研究研究,它的低版本可以考虑。谢谢
    k9982874
        15
    k9982874  
       2024 年 3 月 16 日 via Android
    @abccccabc 你说的不就是爬虫,白嫖就白嫖大方承认不丢人,强行解释就又当又立了
    abccccabc
        16
    abccccabc  
    OP
       2024 年 3 月 16 日
    @k9982874 没玩过爬虫,我是手工复制文章到论坛。有时候因为文章的图片太多,导致图片本地化失败。到目前为止,还没有自动采集过别人的文章入数据库的事情。

    白嫖???工具包我只能白嫖呀,像这样的工具包 Yurunsoft/YurunHttp 我写不出来的,只能用别人写好的工具包。
    UFc8704I4Bv63gy2
        17
    UFc8704I4Bv63gy2  
       2024 年 3 月 17 日
    帖子保存起来然后异步去下载图片,什么年代了还同步取图
    并发取图 curl_multi_exec 推荐,pthread 不推荐坑多
    wpjscc
        18
    wpjscc  
       2024 年 3 月 17 日 via Android
    异步可以试试 reactphp http

    https://github.com/reactphp/http
    abccccabc
        19
    abccccabc  
    OP
       2024 年 3 月 17 日
    沃得天呐,你们到底知道多少这种 PHP 工具包呀?

    看来是我见识少呀,向各路并发/异步 PHP 高手学习学习。展示一下你写的 并发/async php 代码???让我开开眼界呗。
    erquiasz0825
        20
    erquiasz0825  
       2024 年 3 月 17 日
    guzzle 简单好用,默认也是用 curl_multi_exec ,不是多线程也不是多进程,而是用非阻塞 I/O 的原理。
    lasuar
        21
    lasuar  
       2024 年 3 月 17 日
    最近发现的一条 curl 并发命令写法(8 个并发):
    seq 8 | xargs -I {} sh -c 'curl -s http://xxx:3000 %{http_code}&'
    lasuar
        22
    lasuar  
       2024 年 3 月 17 日
    seq 8 | xargs -I {} sh -c 'curl -s http://xxx:3000 %{http_code} &'
    abccccabc
        23
    abccccabc  
    OP
       2024 年 3 月 17 日
    @erquiasz0825 guzzle 简单好用,默认也是用 curl_multi_exec ,不是多线程也不是多进程,而是用非阻塞 I/O 的原理。

    楼上有人建议过,我看了下,他的版本让人真的好纠结:用 v6 版本 php 版本为 >=5.5 and <8.0 ,用 v7 版本, php >= 7.2.5 。

    现在在看 Yurunsoft/YurunHttp
    ben1024
        25
    ben1024  
       2024 年 3 月 17 日
    guzzle ,reactphp 都可以,curl_multi_exec 原生也可以
    zzzyyysss
        26
    zzzyyysss  
       2024 年 3 月 17 日
    这种就该在前端用 js 处理,你的 php 只需要一个上传的接口,前端把图拿出来 然后一个个直接传给 php ,php 返回保存在服务器的地址,然后你再前端用 js 把原来的地址替换,然后保存就 ok
    liaoxx
        27
    liaoxx  
       2024 年 3 月 18 日
    有幸写过一个用 yeild 和 guzzle/http 的请求池写过一个工具类
    /**
    * 异步/并发/批量请求 (慎用)
    * @param string $url 请求地址
    * @param array $allParams 所有请求参数 int[{once request params}]
    * @param array $options 请求配置 ['headers'=>[]]
    * @param callable|null $onSuccess 请求成功回调函数 function(ResponseInterface $response,int $index,array $params){}
    * @param callable|null $onFailure 请求失败回调函数 function(\Exception $exception,int $index,array $params){}
    * @param callable|null $onComplete 请求完成回调函数
    * @return void
    * @author LiaoYongjian
    * @date 2024-01-19 17:43
    */
    public function postAsyncRequests(string $url, array $allParams, array $optiOns= [], callable $OnSuccess= null, callable $OnFailure= null, callable $OnComplete= null)
    {
    $headers = $options['headers'] ?? [];
    $client = $this->getCli();

    //生成器 用于生成异步请求
    $generator = function () use ($client, $allParams, $url, $headers, $onSuccess, $onFailure) {
    foreach ($allParams as $index => $params) {
    yield function () use ($client, $url, $headers, $index, $params, $onSuccess, $onFailure) {
    //异步请求
    return $client->postAsync($url, [
    'headers' => $headers,
    'json' => $params,
    ])->then(
    function ($response) use ($onSuccess, $index, $params) {
    if ($onSuccess) {
    $onSuccess($response, $index, $params);
    }
    return $response;
    },
    function (\Exception $e) use ($onFailure, $index, $params) {
    if ($onFailure) {
    $onFailure($e, $index, $params);
    }
    throw $e;
    }
    );
    };
    }
    };
    //异步请求池
    $pool = new Pool($client, $generator());
    //异步请求池的回调函数
    $respOnses= $pool->promise()->wait();
    if ($onComplete){
    $onComplete($responses);
    }
    }
    cybort
        28
    cybort  
       2024 年 3 月 18 日 via Android
    超时是给你限流了吧,你请求的快限的更快。
    putyy
        29
    putyy  
       2024 年 3 月 18 日
    原生 curl_multi
    putyy
        30
    putyy  
       2024 年 3 月 18 日
    如果资源存的七牛 还可以用七牛云的接口帮下载
    abccccabc
        31
    abccccabc  
    OP
       2024 年 3 月 18 日
    @zzzyyysss

    你说的是这样的吗?
    ```
    var allimg = 获取到的图片数组;
    var oldcOntent= 原内容;
    for(var i=0; i<allimg.length; i++){
    $.post('url', 参数, function(ret) {
    if(ret['code'] == 200) {
    oldcOntent= oldcontent.replace(ret['oldimgurl'], ret['newimgurl']);
    }else{
    console.log(错误信息);
    }
    }, 'json');
    }
    ```

    这样有一个很大的问题:多个异步去修改同一全局变量,必须要锁定全局变量 oldcontent ,然后用队列的形式去替换 oldcontent 内容,不然最后只会有一个修改 oldcontent 生效。

    这样更麻烦,搞不定。

    -------------------
    php 用了并发,现在问题也不小,
    ```
    <?xml version="1.0" encoding="UTF-8"?>
    <Error>
    <Code>AccessDenied</Code>
    <Message>You are denied by bucket referer policy.</Message>
    <RequestId>65F44FE6D4BE2035341DF46B</RequestId>
    <HostId>40114.oss-cn-beijing.aliyuncs.com</HostId>
    <BucketName>blog-picture-240114</BucketName>
    <EC>0003-00000503</EC>
    <RecommendDoc>https://api.aliyun.com/troubleshoot?q=0003-00000503</RecommendDoc>
    </Error>
    ```
    这节奏似乎只能放弃 PHP 并发了。
    abccccabc
        32
    abccccabc  
    OP
       2024 年 3 月 18 日
    @putyy 用不起云存储,个人小论坛,用来记录我的学习而已。

    @cybort 确实是并发超过一定数量后,部分主机会限制。直接返回类似 403 这种
    zzzyyysss
        33
    zzzyyysss  
       2024 年 3 月 18 日
    你为什么要锁定全局变量,你的目标不就是 源代码中的 图片地址远程换本地吗?
    你可以在任何时间点 处理这个操作。
    js 那个方法 你只要在上传完毕之后,用最新的地址 从富文本中替换旧地址就可以了。
    或者你在点击保存按钮之后加个 loading 统一处理。
    之所以推荐你在前端处理是因为有些网站你用后台 php 去抓图片会有防盗链的。
    abccccabc
        34
    abccccabc  
    OP
       2024 年 3 月 18 日
    @zzzyyysss 我试过了,多个异步去修改 oldcontent 替换旧图片地址,只有 for 循环最后 i 变量替换操作生效。 现在我明白,是要对 oldcontent 加锁,这应该就是竞争。太深澳了,又搞不定。唉。


    看来现在得请教 JS 高手了。
    zzzyyysss
        35
    zzzyyysss  
       2024 年 3 月 18 日
    ```
    const images = [];
    const replaceData = {};
    const asyncPost = (url, data) => {
    return new Promise((resolve, reject) => {
    $.post(url, data, (ret) => {
    if(ret['code'] == 200) {
    resolve(ret);
    } else {
    reject('获取图片出错')
    }
    })
    })
    }
    // 每当拿到新的图片时调用
    const OnNewImage= async (oldImgUrl) => {
    if (!replaceData[oldImgUrl]) {
    const ret = await asyncPost(url, data);
    replectData[ret['oldImgUrl']] = ret['newImgUrl'];
    }
    }
    // 在保存之前 处理 content
    const beforeSubmit = (content) => {
    Object.keys.forEach(oldUrl => {
    content.replaceAll(oldUrl, replaceData[newUrl])
    })
    return content;
    }
    ```
    大概就是这样,你可以试试
    xaxb
        36
    xaxb  
       2024 年 4 月 1 日
    没有稳定的原生解决方案, 上层的 composer 里更不会有。
    lyxxxh2
        37
    lyxxxh2  
       2024 年 4 月 18 日
    guzzlephp 就有啊,文档都写着...
    官方 curl 也有,更麻烦。

    https://docs.guzzlephp.org/en/stable/quickstart.html
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1969 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 15:14 PVG 23:14 LAX 07:14 JFK 10:14
    Do have faith in what you're doing.
    div class="sep10">
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86