abccccabc
V2EX  ›  PHP

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

By abccccabc at 2024 年 3 月 16 日 · 4571 次点击
各路 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 的原理。
freemoon
   21
freemoon  
   2024 年 3 月 17 日
最近发现的一条 curl 并发命令写法(8 个并发):
seq 8 | xargs -I {} sh -c 'curl -s http://xxx:3000 %{http_code}&'
freemoon
   22
freemoon  
   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
© 2026 V2EX · 118ms · 3.9.8.5