我有 1000000 个不同的请求(不同的请求我的意思是它有不同的查询参数。它只是一个 GET 请求,没有有效负载。请求和响应的大小仅以 KB 为单位。没有图像或复杂的东西)在一个文本文件中,其中每行都是 cURL,后跟实际的 URL。实际上,每行都可以是 http url,我可以将响应传送到 jq,如果满足条件,我将写入日志文件(这就是我想要实现的目标)。
我计划从 1000 开始,然后达到 500010000 个请求/秒。我们更喜欢持续大约。几个小时(例如 48-72 小时)内每秒 10000 个请求。
哪种方法是最好的?
- 在文本文件上使用 gnu 并行,每个文本文件将有 10000 个准备好的 http url? (这里在文本文件中curl比http更好?)
- 在执行上述操作时,每个curl请求是否在自己的shell中运行?如何将其更改为可以发送请求并接收每秒 10000 个请求的响应的单个 shell?
我在 xargs 和 gnu parallel 之间花了 2.5 天。但在堆栈溢出中,gnu 并行的一些答案适用于 xargs,反之亦然。另外,是否有更好的工具(例如 Python 工具或 ab 工具)可以每秒发送 10000 个请求,并且对于满足条件的每个响应,写入日志文件。请帮忙 。谢谢。
PS:golang 可以比 gnu 并行更好地帮助并行化这项工作吗?
答案1
太长了;博士
您的目标是 Gb/s 速度,您甚至无法足够快地生成进程,更不用说启动 shell。因此 shell 脚本不会是您的解决方案。使用libcurl
允许访问libcurl-multi
.
完整答案
哪种方法是最好的?
- 在文本文件上使用 gnu parallel,每个文本文件将有 10000 个准备好的 http url?(这里的文本文件中 curl 比 http 更好?)
- 在执行上述操作时,每个curl请求是否在自己的shell中运行?如何将其更改为可以发送请求并接收每秒 10000 个请求的响应的单个 shell?
您希望每秒“以 KB 为单位”(复数)执行 10,000 个请求,因此这不仅仅是一根 1Gb/s 以太网电缆。确保您了解其架构含义!
首先,您的传输介质(某些以太网?)是串行的。从最深的技术层面来看,并行发送多个数据包是不可能的。可能的方法是填充要从多个核心发送的数据包队列,然后以并行方式处理传入的回复。
curl
然后:只为了执行一个请求而生成整个进程是极好的效率低下。但生成整个 shell(我猜你正在考虑 bash?)更糟糕! (生成一个进程意味着创建/分叉一个新进程,用可执行文件中的映像替换它,加载可执行文件依赖的所有库,解析配置/启动脚本,然后做真正的工作。在你的情况下,真正的工作是更轻松(我在 C 中的快速测试循环表明,您不能执行超过 ca 3500× 的操作,vfork()
然后检查 PID,然后exec
在 8 线程 3.6 GHz CPU 上每秒执行一个空程序。您想要exec 一些非常重量级的东西。你想要生成我的机器每秒可以生成的进程的 3 倍,并且仍然在这些生成的进程中进行网络和处理,这在很大程度上不会发生。)
jq
为每个请求创建一个流程是完全灾难性的。jq
必须做两件事:
- 解析您作为参数传递的查询,
- 根据该查询解析 JSON。
现在,JSON 每次都不同,但是一旦您知道查询(并且所述查询并不太复杂),解析起来就相对简单,但解析查询本身基本上就是编译程序。您多次进行相同的编译,而实际上,查询只是保持不变。
因此,对于这样的高性能测试,通过 shell 和parallel
/执行此操作xargs
是行不通的 - 您的系统效率低下将根本不允许您以所需的速度工作。
因此,请编写一个程序。编程语言并不重要,只要它允许适当的多线程(也许避免 PHP、Delphi 和嗯,Visual Basic 6.0),并且可以访问相当快的 JSON 解析器。 Python 可以工作,但 Python 并不出名好的多线程。但它可能仍然有效。就我个人而言,我会简单地用 C++ 编写它。
推荐:
如果您觉得您对 JSON 解析器足够了解,知道您宁愿避免处理epoll
细节:libcurl
有一个很好的 API 可以完成这样的工作:libcurl 多多接口。对每个 CPU 核心执行其中一项操作,您与服务器的连接可能会饱和。
如果您想编写一个以复杂性为代价来全局最大化 CPU 利用率的应用程序,您将拥有单独的传输和接收工作线程,因为发送请求可能比处理接收到的数据容易得多。在这方面,你首先要
- 初始化你的多线程安全日志系统(我喜欢 spdlog,但每秒看到 10000 个潜在日志,聚合和写入二进制数据而不是人类可读的文本文件可能更合适),
- 设置你的解析器,
- 产生一堆(疯狂猜测:与 CPU 线程数 /3 - 1 一样多)的传输工作线程 (TX),
- 产生一堆(疯狂猜测:CPU 线程·2/3 - 1)接收工作线程 (RX),
- 生成一个线程,该线程持有准备好读取数据的 TCP 套接字的操作系统通知令牌。在 Linux 上,该机制是
epoll
,在 Python 中可以通过select
模块 as来使用select.epoll
。 - 产生一个线程来准备请求,建立 TCP 连接,然后将它们分配给工作人员的传入队列
在每个 TX 工人中,
- 您发出准备好的请求(这可能只是一个
curl_
函数调用 - libcurl 实际上是一个很好的库,而不仅仅是一个命令行工具)
在每个 RX 工作线程中,
- 您获取刚刚获得的数据并解析它,计算您的结果,如果合适,则告诉您的日志系统进行记录,
在epoll线程中,
- 通过获取数据并将其公平地交给 RX 工作人员(例如以循环方式)来处理事件。
epoll
如果你想要一个使用libcurl的例子,curl
有一个例子(实际上,它非常接近您的用例!)。
如果您想讨论如何在 C++ 中处理多线程、curl 和网络,这Stack Overflow 的答案可能适合你。
答案2
@MarcusMiller 很好地总结了为什么您不应该期望能够从一台机器每秒执行 10000 个请求。但也许你并没有问这个。也许您有 100 台机器可以用于执行此任务。然后它就变得可行了。
在 100 台机器中的每台机器上运行如下内容:
ulimit -n 1000000
shuf huge-file-with-urls |
parallel -j1000 --pipe --roundrobin 'wget -i - -O - | jq .'
如果您需要多次运行 url,只需添加更多shuf huge-file-with-urls
.或者:
forever shuf huge-file-with-urls | [...]
(https://gitlab.com/ole.tange/tangetools/-/tree/master/forever)
GNU Parallel 可以并行运行 1000 多个作业,但您的计算机不太可能处理这么多作业。即使刚刚开始 1000000sleep
秒(参见:https://unix.stackexchange.com/a/150686/366317)将我的 64 核机器推向了极限。启动 1000000 个并发进程,实际上做过某些事情会使服务器完全停止。
进程将从一个核心迁移到另一个核心,因此您无法分辨哪个核心正在运行什么。您可以使用 限制进程应在哪个核心上运行taskset
,但这仅在非常特殊的场景中才有意义。通常,您只是希望将任务转移到空闲的 CPU 核心,而内核可以很好地完成这一任务。
答案3
@MarcusMiller 很好地总结了为什么您不应该期望能够从一台机器每秒执行 10000 个请求。
我想我必须修改一下。
我刚刚在我的 64 核机器上安装了varnish
andab
然后运行:
$ seq 64 | parallel -N0 ab -c 100 -n 100000 http://lo:6081/
Server Software: Apache/2.4.41 (really Varnish)
Server Hostname: lo
Server Port: 6081
Document Path: /
Document Length: 10918 bytes
Concurrency Level: 100
Time taken for tests: 132.916 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 1127168742 bytes
HTML transferred: 1091800000 bytes
Requests per second: 752.35 [#/sec] (mean)
Time per request: 132.916 [ms] (mean)
Time per request: 1.329 [ms] (mean, across all concurrent requests)
Transfer rate: 8281.53 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 94 484.9 1 15453
Processing: 0 38 68.9 28 4672
Waiting: 0 37 68.9 27 4672
Total: 1 132 498.4 29 15683
我每秒收到 64*720 个请求 = 大约 45000 个请求和回复。
每个回复为 10 KB = 4.5 Gbit/s
大约 50% 的 CPU 是varnish
.剩下的就是ab
。
我觉得你能在 16 核机器上以 1 Gbps 的速度执行 10000 个请求/秒。但是您无法对结果进行任何处理,而且我没有找到一种方法来告诉ab
使用不同的请求。
如果你使用https://github.com/philipgloyne/apachebench-for-multi-url你甚至可以使用不同的网址:
parallel --pipepart --block -1 --fifo -a urls ./ab -L {} -c 100 -n 100000