【问题标题】:How to reliably reproduce curl_multi timeout while testing public proxies如何在测试公共代理时可靠地重现 curl_multi 超时
【发布时间】:2019-07-16 13:35:27
【问题描述】:

相关信息:issue 3602 on GitHub

我正在做一个收集和测试公共/免费代理的项目,并注意到当我使用 curl_multi 接口测试这些代理时,有时会收到很多 28(timeout) 错误。如果我单独测试每个代理,这种情况永远不会发生。

问题在于这个问题的重现性不可靠,而且它不会总是出现,它可能是 curl 中的东西或其他东西

不幸的是,我不是那么深的网络调试器,我不知道如何在更深层次上调试这个问题,但是我写了 2 个 C 测试程序(其中一个最初是 written by Daniel Stenberg 但我修改了它输出为与其他 C 程序相同的格式)。这 2 个 C 程序使用 curl 测试 407 个公共代理

  1. 带有 curl_multi 接口(有问题)

  2. 在许多线程上使用 curl,每个 curl 都在一个线程上运行。 (没问题)

These are the 2 C programs I wrote for testing我不是 C 开发人员,所以请告诉我您在这 2 个程序中发现的任何错误。

这是我一个月前用来重现问题的original PHP class

还有these are the 2 C programs tests results。您可以注意到使用 curl_multi 超时完成的测试,而 curl-threads 进行的超时是稳定的(大约 407 个代理中的 50 个正在工作)。

这是来自测试结果的样本。 请注意第 4 列和第 5 列,了解 curl 线程如何超时约 170 次并成功连接约 40 次。其中,curl_multi 在 407 个代理中实现了 0 次成功连接和约 300 次超时。

column(1) : #
column(2) : time(UTC)
column(3) : total execution time (seconds)
column(4) : no error 0 (how many requests result in no error CURLE_OK)
column(5) : error 28 (how many requests result in error 28 CURLE_OPERATION_TIMEDOUT)
column(6) : error 7 (how many requests result in error 7 CURLE_COULDNT_CONNECT)
column(7) : error 35 (how many requests result in error 35 CURLE_SSL_CONNECT_ERROR)
column(8) : error 56 (how many requests result in error 56 CURLE_RECV_ERROR)
column(9) : other errors (how many requests result in errors other than the above)
column(10) : program that used the curl
column(11) : cURL version

c(1)    c(2)           c(3)c(4)c(5)c(6)c(7)c(8)c(9) c(10)                  c(11)
267 2019-3-28 01:58:01  40  43  176 183 1   4   0   C (curl - threads) (Linux Fedora)   7.59.0
268 2019-3-28 01:59:01  30  0   286 110 1   10  0   C (curl-multi one thread) (Linux Fedora)    7.59.0
269 2019-3-28 02:00:01  30  46  169 181 1   8   2   C (curl - threads) (Linux Fedora)   7.59.0
270 2019-3-28 02:01:01  31  0   331 74  1   1   0   C (curl-multi one thread) (Linux Fedora)    7.59.0
271 2019-3-28 02:02:01  30  42  173 186 1   4   1   C (curl - threads) (Linux Fedora)   7.59.0
272 2019-3-28 02:03:01  30  0   277 116 1   13  0   C (curl-multi one thread) (Linux Fedora)    7.59.0

为什么 curl_multi 超时与大多数连接不一致,而 curl-threads 从不这样做?

我下载了 Wireshark 并在 2 个 C 程序运行时用它来捕获流量,我还 filtered 到 2 个 C 程序使用的代理列表的流量,并将 files 保存在 GitHub 上。

curl-threads 程序(预期行为)

407 个代理中有 63 个成功连接和 158 个连接超时。

curl_multi 程序(un预期的行为)

407 个代理中有 0 个成功连接和 272 个连接超时。

您可以使用 Wireshark 打开 .pcapng 文件并查看我的计算机上记录的流量,同时出现预期/意外行为。我过滤了到 407 代理 IP 的流量,并在 30 秒的 curl 限制后让 Wireshark 打开了一会儿,因为我注意到一些数据包仍然出现。我不了解 Wireshark 和这种级别的网络,但我认为这可能很有用。


带宽注意事项:

在wireshark中打开curl_threads程序的.pcapng文件(正常行为),然后转到Statistics > Conversations。你会看到这样的窗口

我已经将数据复制并保存在Github上here,现在计算从A->B和B->A发送的字节的Sum

正常工作所需的整个带宽约为 692.8 KB。

【问题讨论】:

  • 请查看我对 GitHub 问题的评论。此外,在您的代码中,最好启用CURLOPT_VERBOSE。为了保持一致性,使用 badger 在 GitHub 上提供的 C 版本也可能相当重要。
  • 你好@JL2210 我有replied 对你在 GitHub 上的评论。关于 C 版本,我只是添加了聚合测试结果并将它们打印到与线程程序格式相同的文件的功能,因此我可以将两个程序的结果放在同一个文件中并进行比较。
  • 我想我已经进行了编辑,使您的问题和您的情况更加清晰。请查看并回复我。
  • @JL2210 非常感谢您的编辑,我会检查它们。 “你的网络上是否有防火墙?或者可能限制出站连接的东西” 如果我的网络有问题,那么 curl-threads 程序也会有,但线程程序可以正常工作curl-multi 程序有时会重现该问题。
  • @JL2210 同样,我制作了一个小 C 程序,它使用 curl 在 1 个线程上进行 1 个请求,它工作正常。 curl_multi 仍然产生28 超时错误,我不再使用了。

标签: php c curl bug-tracking curl-multi


【解决方案1】:

我得到了可重现的行为,我正在等待 GitHub 上的 badger 回复。尝试运行 Ettercap 之类的程序以获取更多信息。

【讨论】:

  • 我使用 Wireshark 捕获了正常和正常行为时的流量,并保存了 Wireshark .pcapng,以便任何人都可以分析发生了什么。我会更新问题和 GitHub 问题,希望这也能有所帮助。
  • 好的。我不能使用 Wireshark,它需要 Qt5,它还没有为我正确构建。当我得到任何信息时,我会用更多信息更新它。
  • “所以回答你的问题:在非常慢/低带宽的网络上运行这些测试。”... 那么,为什么线程程序在相同的机器和网络?!他们在成功连接方面表现不断curl-threads>43, curl-multi>0, curl-threads>46, curl-multi>0, curl-threads>42, curl_multi>0, ...
  • 我在慢速/低带宽网络上不断收到错误,所以这不能回答您的问题吗?。
  • “我在慢速/低带宽网络上不断收到错误,所以这不能回答你的问题吗?” 不詹姆斯,因为线程程序 工作! .在非常慢的网络上,offcurse 两者都不起作用,但在良好的连接上,有时只有 curl_multi 程序会显示问题,它甚至可以在具有 500 Mbps 上行链路的生产服务器上重现!
【解决方案2】:

在我看来,curl 本身没有问题,但如果连接被拒绝,则同时与代理服务器进行太多连接。您可能会被永久或一段时间列入黑名单。

通过从当前 IP 运行 curl 并执行 stat 检查:建立了多少连接,拒绝了多少,超时了多少。做几次并收集平均值。然后将服务器更改为具有不同 IP 的其他服务器,并检查您那里的统计信息。在第一次运行时,您应该有更好的统计数据,如果您在新 IP 上重复测试,可能只会变得更糟。 好主意可能是不要使用所有代理池来连接进行统计,而是从它们中选择一个切片并检查实际 IP 并重复检查新 IP,因此如果原因是您滥用服务,则不要将自己列入黑名单所有代理,但如果确实如此,仍然有下一组“未触及”代理在新 IP 上对其进行测试。 请注意,即使代理的 IP 位于不同的位置,它们也可以属于同一个服务提供商。这可能对他们的所有代理服务都有一个滥用列表,因此,如果您在一个国家/地区所做的请求数量不理想,即使在您连接到另一个国家/地区代理之前,您也可能在其他国家/地区被阻止。

如果您仍然想检查这是否不是 curl,那么您可以设置一个包含多个服务的测试环境。您可以将此测试环境传递给 curl 维护者,以便他可以复制错误。 你可以使用docker创建10个、20个或100个代理服务器并连接它们,看看curl是否有问题。

你需要 docker可以安装在Win/Mac/Linux
创建代理的proxy image 之一
为容器创建网络tutorial(网桥应该没问题)
将容器附加到网络 --network
很好地为每个代理容器设置他们的--ip
使用--volume
mountig 错误日志/配置文件/目录,使每个代理容器都可以读取配置和写入错误日志(这样您就可以阅读为什么发生这种情况时它们会断开连接) 并且所有代理容器都应该运行

您可以通过两种方式连接到在容器内运行的代理。 如果您想在这些容器之外使用 curl,那么您需要使用 -p 将这些代理的端口从容器公开到外部世界(在您的情况下为 curl)。

您可以使用另一个具有 linux + curl 的容器映像。例如Alpine linux + curl 并以与代理相同的方式将其连接到同一网络。如果你这样做了,你就不需要发布(公开)代理端口,也不需要考虑我应该为这个特定的代理公开多少代理端口。

在每一步你都可以发出一个命令

docker ps -a

查看所有容器及其状态。

停止并删除所有容器(不是它们来自的映像,而是正在运行的容器),以防您退出的容器出现一些错误。

docker stop $(docker ps -aq) && docker rm $(docker ps -aq)

或停止并从列表中删除特定容器

docker stop <container-id>
docker rm <container-id>

查看所有连接到桥接网络的容器(默认)

docker network inspect bridge

如果您确认与本地计算机上的代理的连接确实存在问题,那么这就是 curl 的维护者可以复制的东西。

只需将上述所有命令创建所有代理,将它们连接到网络等文件中,例如replicate.sh

开头的脚本
#!/bin/sh

and your comands here

保存该文件并发出命令

chmod +x ./replicate.sh

使其可执行。

您可以运行它来仔细检查一切是否按预期工作

./replicate.sh

并将 curl 的维护者发送到您遇到问题的复制环境。

如果您不喜欢使用 docker run 等大量命令来运行代理,您可以使用 docker compose 代替,这样您就可以在一个文件中定义整个测试环境。

如果您运行大量容器,您可以限制资源,例如 memory 每个容器消耗的资源,如果代理太多,可能会对您有所帮助

【讨论】:

  • 非常感谢 Jimmix 抽出宝贵时间。排除了我被列入黑名单的可能性,因为 curl-threads 程序在 SAME NETWORK 上运行良好,而 curl-multi 程序不起作用,幸运的是我在测试结果中我在问题中提供了一个示例,curl-multi 根本不起作用(0 个成功连接)。检查我在答案中提供的示例中的第 4 列,您会看到 curl-threads&gt;43, curl-multi&gt;0, curl-threads&gt;46, curl-multi&gt;0, curl-threads&gt;42, curl_multi&gt;0, ...(每次测试之间的时间跨度为 1 分钟)。
  • 检查C程序的readme,如果你愿意,你会看到如何自己做测试,你就会明白我的意思。
  • 非常感谢 docker 的建议,但我认为我不需要它,因为问题在当前安装中已经可以重现,只是并不总是可以重现。我从未使用过 Docker,但是当我来尝试 docker 时,我会将您的答案添加到书签中作为参考:) 谢谢。
  • @Accountantم 我很高兴您重现了该错误。我阅读了您链接的自述文件和 cron 作业。您可能还喜欢this way 对每偶数/奇数分钟使用 cron。
  • 这不是黑名单问题,因为代理不知道他是使用单线程的 curl_multi api,还是使用每个句柄 1 个线程的多个 curl_easy 句柄,并且此问题仅在以下情况下发生他在单线程中使用 curl_multi api,当他使用 1-thread-per-easy-handle 时不会发生这种情况。如果这是一个黑名单问题,那么在使用 1-thread-per-handle 时也会发生这种情况,但事实并非如此。
猜你喜欢
  • 1970-01-01
  • 2013-06-21
  • 1970-01-01
  • 2013-10-23
  • 2013-09-14
  • 1970-01-01
  • 2013-09-01
  • 2012-12-06
相关资源
最近更新 更多