一个可以在 PHP -FPM 环境来并发访问 HTTP 接口的工具类

2021-04-26 20:13:20 +08:00
 sun2920989

https://github.com/CodeApePro/TcpMockHttp

这并不是一个开源的项目,只是个人在使用的一段工具类代码.

原理是通过 sockets 扩展创建 tcp 连接,在 tcp 连接上传输符合 http1.1 协议的数据,从而将发送数据与读取结果分开.

发出来以供遇到类似场景时参考.

2287 次点击
所在节点    PHP
45 条回复
MarlonFan
2021-04-26 21:36:11 +08:00
Guzzle 的 asnyc request 好像就是做这个的
sun2920989
2021-04-26 21:58:56 +08:00
@MarlonFan 还是有点区别的, Guzzle 和 curl_multi 给我的感觉差不多.当然我只是做了简单测试,最后并没有在项目用引用 Guzzle.
sxbxjhwm
2021-05-08 15:12:37 +08:00
我前阵子也封装了一个。。不过是基于 curl_multi 的
https://github.com/jshensh/php-curl-class#%E5%B9%B6%E5%8F%91%E8%AF%B7%E6%B1%82
sun2920989
2021-05-08 15:17:25 +08:00
@sxbxjhwm #3 之前考虑过,主要是觉得不够灵活,而且无法在逻辑里面向上跳出.所以才换了个思路用了这个方式.
sxbxjhwm
2021-05-08 15:19:56 +08:00
@sun2920989 curl_multi_exec 执行完后直接干别的就可以了阿,判断和获取主要在这段上 https://github.com/jshensh/php-curl-class/blob/master/src/Multi.php#L110-L152
sun2920989
2021-05-08 15:24:06 +08:00
@sxbxjhwm #5 我说的向上跳出的意思是,某个方法需要对 http 响应的结果进行处理,但是又不希望在这个方法里就阻塞获取结果.这时候就需要用回调或 yield 向上跳出,如果使用 curl_multi 的话,我没有什么思路来处理这个场景.
sxbxjhwm
2021-05-08 15:28:25 +08:00
@sun2920989 curl_multi_exec 不是阻塞的,后续的获取可以分开写,你是想单独先抽某个结果出来用还是?
sxbxjhwm
2021-05-08 15:35:46 +08:00
@sun2920989 curl_multi_exec 通过第二个引用传参 &$still_running 来告诉你栈中还有多少正在执行的 curl 句柄。阻塞的是后续获取的那部分 while 段源码,它通过判断 $still_running 来确认有没有全部执行完,同时在 while 循环体内也有通过 curl_multi_exec 对 $still_running 重新赋值的操作,所以在什么阻塞获取是完全可以由你自己决定的。
sun2920989
2021-05-08 15:37:07 +08:00
@sxbxjhwm #7 简单来说,比如原有的业务流程有个方法,内容是请求一次数据,对这份数据进行一些逻辑处理,然后返回.此时如果需要多次调用这个方法,当然是每个请求顺序执行的.如果此时遇到业务效率瓶颈,期望这个方法是可以并发的,那么此时只实现 http 请求时的并发是不够的,必须要将整个方法并发,如果使用 curl_multi 之类的方案的话,必须要将原有的方法彻底重写来直接支持批量获取数据批量处理数据批量返回,对于现有业务代码这些修改是很大的,我选择的方案是保持这个方法每次还是只处理一次请求的逻辑,通过回调跳出的方式来实现.具体的我在我项目的 demo.php 中的最后一个示例简单举了个例子.
sxbxjhwm
2021-05-08 15:43:20 +08:00
@sun2920989 你是希望预处理数据返回阿,那 curl_multi_info_read 和 curl_multi_getcontent 完全可以满足你的需求的,具体可以看这一段 https://github.com/jshensh/php-curl-class/blob/master/src/Multi.php#L118-L129 。我在这里就是将原有的 curl 句柄进行处理后放在待返回的数组里最后统一扔出来的。如果需要更细致的并发我可能就是采用 pcntl_ 系列方法处理了
sun2920989
2021-05-08 15:50:06 +08:00
@sxbxjhwm #10 其实并不能满足我的需求的,单单在 exec 之前要组织全部的请求,不能再后续添加,就无法应用在我这边的场景下.就像我刚才说的,除非经过彻底的改写原来的方法,直接支持批量参数过来,否则是无法直接使用 curl_multi 的.
sun2920989
2021-05-08 15:55:20 +08:00
@sxbxjhwm #10 之所以选择使用 tcp 连接,就是因为对于一条连接中,产生的每一次动作都是真实的,建连,发送数据,获取数据,执行了就会有效果.而对于 curl_multi 而言,我可以简单的理解为在 exec 之前,其实一切都没有发生,都是从 exec 开始才执行,那么,在一个原有的业务调用链中,什么时候进行 exec 就是一个无法解决的问题.
sxbxjhwm
2021-05-08 16:09:40 +08:00
@sun2920989 你没看我封装的源码。。我实现了请求错误时的重试机制,原理就是在 exec 后继续使用 curl_multi_add_handle 向栈里推句柄实现的
sun2920989
2021-05-08 16:20:16 +08:00
@sxbxjhwm #13 但是在 exec 之后再次 add 的,就不是并发的了,只能在下一次 exec 执行了.
sun2920989
2021-05-08 16:23:00 +08:00
@sxbxjhwm #13 假设一个方法原来是发送一次请求的,改为使用 curl_multi 之后,是否要在方法内 exec,如果 exec 那么多次调用这个方法就不是并发的,如果不 exec,那么在什么时候 exec 呢,期望调用这个方法的人在调用一次或多次这个方法后再次主动调用一个执行 exec 的方法吗?
sun2920989
2021-05-08 16:30:43 +08:00
@sxbxjhwm #13 按照我在 demo 里面写的例子扩展一下,可以写出类似下面的代码

function testGetData(){

$result = Pool::call('GET','http://api.ipify.org/?format=json');

$decode_function = function($result){

return json_decode($result,true);

};

return new Decode($result,$decode_function);
}

$result1 = testGetData();

$result2 = testGetData();

Helper::wait($result1);

Helper::wait($result2);


这里面 testGetData 是可以被随意调用多次的,无需提前准备好全部调用,而且这个方法本身是可并发的,并且可以对返回值进行处理.如果使用 curl_multi 或者按照你的项目的示例的话,我不知道如何来实现这个效果.
sun2920989
2021-05-08 16:41:18 +08:00
@sxbxjhwm #13 还有一个问题是连接数量的限制,对于我们的业务场景而言,并发连接数是有上限的,比如 api 端并发限制设置为 10,我需要调用 50 次获取 50 个不同的数据,直接发送过去是必然要很多 503 的,直接使用原始 tcp 连接的话可以做到在固定的 tcp 连接数量的情况下来传递更多的请求.这个也是 curl_multi 很难控制的.
sxbxjhwm
2021-05-08 17:39:16 +08:00
@sun2920989 curl_multi_exec 本来就是 while 在一直调用的,它只是启动这个栈中所有的 curl 句柄而已 https://www.php.net/manual/zh/function.curl-multi-exec.php ,限制并发相关的源码在我之前贴的 GitHub 链接里其实都有,如果你实际测试一下就会发现它也能很好做到并发控制与重试的。
sun2920989
2021-05-08 18:12:28 +08:00
@sxbxjhwm #18 我之前试用过,可能了解的不够深入,但是确实无法实现我的要求,示例代码上面已经贴了,我无法使用 curl_multi 来做到这个效果,而这种效果对于我的项目来说是必要的.所以如果可以实现,您可以试着贴一段示例代码我借鉴一下.
sxbxjhwm
2021-05-08 18:28:26 +08:00
@sun2920989 等周一吧,我发个 gist

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/773442

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX