太长了;博士

太长了;博士

我有 1000000 个不同的请求(不同的请求我的意思是它有不同的查询参数。它只是一个 GET 请求,没有有效负载。请求和响应的大小仅以 KB 为单位。没有图像或复杂的东西)在一个文本文件中,其中每行都是 cURL,后跟实际的 URL。实际上,每行都可以是 http url,我可以将响应传送到 jq,如果满足条件,我将写入日志文件(这就是我想要实现的目标)。

我计划从 1000 开始,然后达到 500010000 个请求/秒。我们更喜欢持续大约。几个小时(例如 48-72 小时)内每秒 10000 个请求。

哪种方法是最好的?

  1. 在文本文件上使用 gnu 并行,每个文本文件将有 10000 个准备好的 http url? (这里在文本文件中curl比http更好?)
  2. 在执行上述操作时,每个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.

完整答案

哪种方法是最好的?

  1. 在文本文件上使用 gnu parallel,每个文本文件将有 10000 个准备好的 http url?(这里的文本文件中 curl 比 http 更好?)
  2. 在执行上述操作时,每个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必须做两件事:

  1. 解析您作为参数传递的查询,
  2. 根据该查询解析 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 核机器上安装了varnishandab然后运行:

$ 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

相关内容