<p>请求流式传输大型二进制响应的性能很差</p>

创建于 2014-12-05  ·  40评论  ·  资料来源: psf/requests

https://github.com/alex/http-client-bench包含我使用的基准。

结果类似于:

| | 请求/http | 插座 |
| --- | --- | --- |
| CPython | 12MB/秒 | 200MB/秒 |
| pypy | 80MB/秒 | 300MB/秒 |
| 去 | 150MB/秒 | 不适用 |

与套接字相比,请求会带来相当大的开销,尤其是在 CPython 上。

Propose Close

所有40条评论

该开销出乎意料地大。 但是,避免它可能会很棘手。

最大的问题是我们对每个块进行了大量处理。 这就是堆栈的所有方式:请求、urllib3 和 httplib。 看看时间都花在了哪里来找出导致效率低下的原因,这将是非常有趣的。

猜测下一步是尝试分析 httplib / urllib3 以查看
表现在那里?

凯文·伯克
电话:925.271.7005 | 二十毫秒.com

2014 年 12 月 4 日星期四下午 5:01,Cory Benfield通知@ github.com
写道:

该开销出乎意料地大。 但是,避免它可能会很棘手。

最大的问题是我们对每个块进行了大量处理。 那是
一直到堆栈:requests、urllib3 和 httplib。 这将是
看看时间都花在了谁身上,这非常有趣
导致效率低下。


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65732050
.

刚刚使用 urllib3 运行了基准测试:

PyPy:120MB/秒
CPython:70MB/秒

我重新运行 CPython + 请求:35MB/s

(我的机器在基准测试中似乎遇到了一点噪音,如果有人有一个更安静的系统,他们可以打开这些系统,那就太棒了)

我尝试在我的机器上关闭所有其他机器后运行这些
应用程序和终端窗口,也有相当多的噪音 -
套接字基准测试从 30mb/s 到 460mb/s 不等。

凯文·伯克
电话:925.271.7005 | 二十毫秒.com

2014 年 12 月 4 日星期四晚上 9:24,Alex Gaynor通知@ github.com
写道:

刚刚使用 urllib3 运行了基准测试:

PyPy:120MB/秒
CPython:70MB/秒

我重新运行 CPython + 请求:35MB/s

(我的机器似乎在基准测试中遇到了一点噪音,如果
任何人都有一个更安静的系统,他们可以使用这些系统,那太棒了)


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65748982
.

我现在让基准测试更容易运行,所以其他人希望可以验证我的数字:

CPython:

BENCH SOCKET:
   8GiB 0:00:22 [ 360MiB/s] [======================================================>] 100%
BENCH HTTPLIB:
   8GiB 0:02:34 [53.1MiB/s] [======================================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:30 [90.2MiB/s] [======================================================>] 100%
BENCH REQUESTS
   8GiB 0:01:30 [90.7MiB/s] [======================================================>] 100%
BENCH GO HTTP
   8GiB 0:00:26 [ 305MiB/s] [======================================================>] 100%

pypy:

BENCH SOCKET:
   8GiB 0:00:22 [ 357MiB/s] [======================================================>] 100%
BENCH HTTPLIB:
   8GiB 0:00:43 [ 189MiB/s] [======================================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:07 [ 121MiB/s] [======================================================>] 100%
BENCH REQUESTS
   8GiB 0:01:09 [ 117MiB/s] [======================================================>] 100%
BENCH GO HTTP
   8GiB 0:00:26 [ 307MiB/s] [======================================================>] 100%

呃……这些数字很奇怪。 CPython 的 httplib 比 requests 或 urllib3 慢,即使两个库都使用 httplib? 那是不对的。

他们一直为我重现——你能试试基准测试,看看是否
你能繁殖吗? 假设您可以,您是否发现
基准?

2014 年 12 月 5 日星期五上午 11:16:45 Cory Benfield通知@github.com
写道:

呃……这些数字很奇怪。 CPython 的 httplib 比请求慢或
urllib3,即使两个库都使用 httplib? 那是不对的。


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65821989
.

我现在只是在拿一台已知安静的机器。 应该需要几分钟才能可用,因为它是一个必须安装的物理盒子(上帝,我爱 MAAS)。

CPython 2.7.8

BENCH SOCKET:
   8GiB 0:00:26 [ 309MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:02:24 [56.5MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:42 [79.7MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:45 [77.9MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:27 [ 297MiB/s] [================================>] 100%

物有所值:

这个补丁CPython 3.4.2

BENCH SOCKET:
   8GiB 0:00:27 [ 302MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:00:53 [ 151MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:00:54 [ 149MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:00:56 [ 144MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:31 [ 256MiB/s] [================================>] 100%

你应该能够在 Python2 上获得同样的效果
env PYTHONUNBUFFERED=-u标志。

2014 年 12 月 5 日星期五上午 11:42:36 Corey Farwell通知@github.com
写道:

物有所值:

这个补丁https://gist.github.com/frewsxcv/1c0f3c81cda508e1bca9,CPython
3.4.2:

工作台插座:
8GiB 0:00:27 [ 302MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:00:53 [ 151MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:00:54 [ 149MiB/s] [================================>] 100%
板凳要求
8GiB 0:00:56 [ 144MiB/s] [==================================>] 100%
板凳去 HTTP
8GiB 0:00:31 [ 256MiB/s] [================================>] 100%


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65826239
.

@alex有趣的是, env PYTHONUNBUFFERED=-u对 Python 2 都没有相同的效果。我的机器传入的结果。

好的,下面的数据来自一台只运行这些测试的机器。 最后一个测试是在设置了 Python -u标志的情况下运行的,正如您所看到的,该标志没有任何作用。

Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 500MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:32 [88.6MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:21 [ 100MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:21 [ 385MiB/s] [================================>] 100%
Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 503MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:33 [87.8MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:22 [99.3MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:20 [ 391MiB/s] [================================>] 100%
Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 506MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:31 [89.1MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:21 [ 389MiB/s] [================================>] 100%

这些数字非常稳定,并具有以下特点:

  1. 原始套接字读取速度很快(废话)。
  2. Go 大约是原始套接字读取速度的 80%。
  3. urllib3 大约是原始套接字读取速度的 20%。
  4. requests 比 urllib3 稍慢,这是有道理的,因为我们添加了几个堆栈帧供数据通过。
  5. httplib 比 requests/urllib3 慢。 那是不可能的,我怀疑我们必须以 httplib 不是的方式配置 httplib 或套接字库。

FWIW,我刚刚从@kevinburke合并添加buffering=True ,做你的跑步
包括那个?

2014 年 12 月 5 日星期五下午 12:04:40 Cory Benfield通知@github.com
写道:

好的,下面的数据来自一台什么都不做的机器
但运行这些测试。 最后一个测试是使用 Python -u 标志运行的
设置,正如您所看到的那样,该标志无效。

蟒蛇 2.7.6
转到版本 go1.2.1 linux/amd64
工作台插座:
8GiB 0:00:16 [ 500MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:01:32 [88.6MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳要求
8GiB 0:01:21 [ 100MiB/s] [================================>] 100%
板凳去 HTTP
8GiB 0:00:21 [385MiB/s] [================================>] 100%

蟒蛇 2.7.6
转到版本 go1.2.1 linux/amd64
工作台插座:
8GiB 0:00:16 [ 503MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:01:33 [87.8MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳要求
8GiB 0:01:22 [99.3MiB/s] [================================>] 100%
板凳去 HTTP
8GiB 0:00:20 [ 391MiB/s] [================================>] 100%

蟒蛇 2.7.6
转到版本 go1.2.1 linux/amd64
工作台插座:
8GiB 0:00:16 [ 506MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:01:31 [89.1MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳要求
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳去 HTTP
8GiB 0:00:21 [389MiB/s] [================================>] 100%

这些数字非常稳定,并具有以下特点:

  1. 原始套接字读取速度很快(废话)。
  2. Go 大约是原始套接字读取速度的 80%。
  3. urllib3 大约是原始套接字读取速度的 20%。
  4. requests 比 urllib3 稍慢,这是有道理的,因为我们
    为要通过的数据添加几个堆栈帧。
  5. httplib 比 requests/urllib3 慢。 那简直是不可能的
    我怀疑我们必须配置 httplib 或 sockets 库
    httplib 不是的一种方式。


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65829335
.

Cory - 查看最新版本的 bench 客户端
buffering=True 在 httplib 中(如 requests/urllib3 所做的那样)

凯文·伯克
电话:925.271.7005 | 二十毫秒.com

2014 年 12 月 5 日星期五上午 10:04,Cory Benfield通知@ github.com
写道:

好的,下面的数据来自一台什么都不做的机器
但运行这些测试。 最后一个测试是使用 Python -u 标志运行的
设置,正如您所看到的那样,该标志无效。

蟒蛇 2.7.6
转到版本 go1.2.1 linux/amd64
工作台插座:
8GiB 0:00:16 [ 500MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:01:32 [88.6MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳要求
8GiB 0:01:21 [ 100MiB/s] [================================>] 100%
板凳去 HTTP
8GiB 0:00:21 [385MiB/s] [================================>] 100%

蟒蛇 2.7.6
转到版本 go1.2.1 linux/amd64
工作台插座:
8GiB 0:00:16 [ 503MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:01:33 [87.8MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳要求
8GiB 0:01:22 [99.3MiB/s] [================================>] 100%
板凳去 HTTP
8GiB 0:00:20 [ 391MiB/s] [================================>] 100%

蟒蛇 2.7.6
转到版本 go1.2.1 linux/amd64
工作台插座:
8GiB 0:00:16 [ 506MiB/s] [================================>] 100%
工作台 HTTPLIB:
8GiB 0:01:31 [89.1MiB/s] [================================>] 100%
工作台 URLLIB3:
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳要求
8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
板凳去 HTTP
8GiB 0:00:21 [389MiB/s] [================================>] 100%

这些数字非常稳定,并具有以下特点:

  1. 原始套接字读取速度很快(废话)。
  2. Go 大约是原始套接字读取速度的 80%。
  3. urllib3 大约是原始套接字读取速度的 20%。
  4. requests 比 urllib3 稍慢,这是有道理的,因为我们
    为要通过的数据添加几个堆栈帧。
  5. httplib 比 requests/urllib3 慢。 那简直是不可能的
    我怀疑我们必须配置 httplib 或 sockets 库
    httplib 不是的一种方式。


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65829335
.

是的,这修复了 httplib 的性能行为,使其更有意义。

新结果和结论:

Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 499MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:12 [ 113MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:21 [ 100MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:20 [ 391MiB/s] [================================>] 100%
  1. 原始套接字读取速度很快(废话)。
  2. Go 大约是原始套接字读取速度的 80%。
  3. httplib 不到原始套接字读取速度的 25%。
  4. urllib3 大约是原始套接字读取速度的 20%,为 httplib 增加了一些小开销。
  5. requests 比 urllib3 稍慢,这是有道理的,因为我们添加了几个堆栈帧供数据通过。

因此,可以说这里的真正成本是 httplib。 加快速度需要让 httplib 不碍事。

不过,我很想知道 httplib 的哪一部分让我们付出了代价。 我认为分析bench_httplib.py是一个很好的下一步。

通过将该行添加到bench_socket.py测试中,我已经排除了通过socket.makefile将套接字转换为文件对象的可能性,这根本不会减慢它的速度。 奇怪的是,它似乎使它更快。

答案几乎可以肯定是传输编码:分块处理。
参见: https :
服务器上的 Content-Length 会产生一些意想不到的结果。

2014 年 12 月 5 日星期五下午 12:24:53 Cory Benfield通知@github.com
写道:

因此,可以说这里的真正成本是 httplib。 加快这一进程需要
让 httplib 不碍事。

不过,我很想知道 httplib 的哪一部分让我们付出了代价。 一世
认为分析 bench_httplib.py 是一个很好的下一步。


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65831653
.

有趣的。

分块处理几乎肯定是问题所在,我对 go 处理得更好并不感到惊讶,尤其是分块是 go 的默认 HTTP 模式。

然而,请求比原始套接字快是......出乎意料!

值得注意的一件事:如果套接字在之前的测试中没有解码分块编码,那么它就获得了不公平的优势,因为它实际上读取的数据比其他方法少! 他们都在读取分块的标头以及 8GB 的​​数据。

这就引出了一个后续问题:我们是否仍然认为所有这些方法实际上都在读取相同数量的数据?

是的,套接字层在作弊,它没有解码分块的元数据,
从技术上讲,阅读量要少一些。 它在那里作为“多快
我们可以阅读吗”,而不是为了证明任何事情。

2014 年 12 月 5 日星期五下午 12:33:10 Cory Benfield通知@github.com
写道:

有趣的。

分块处理几乎肯定是问题所在,我并不是真的
惊讶于 go 处理得更好,特别是因为 chunked 是默认的
去的 HTTP 模式。

然而,请求比原始套接字快是......出乎意料!

值得注意的一件事:如果套接字没有解码分块编码
在之前的测试中,它获得了不公平的优势,因为它实际上是
读取的数据比其他方法少! 他们都在读
分块的标头以及 8GB 的​​数据。

这就引出了一个后续问题:我们是否仍然认为所有这些方法
实际上读取相同数量的数据?


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65833299
.

如果这与我们一次从套接字读取的块大小有关,我不会感到惊讶。

@alex 的蛋糕非常有帮助:蛋糕:

@nelhage对各种示例进行了一些跟踪(在传输中
编码:分块大小写) https://gist.github.com/nelhage/dd6490fbc5cfb815f762
是结果。 看起来 httplib 中有一个错误导致它
并不总是从套接字读取完整的块。

2014 年 12 月 8 日星期一上午 9:05:14,Kenneth Reitz通知@github.com
写道:

@alex https://github.com/alex 的蛋糕,非常有帮助 [图片:
:蛋糕:]


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -66147998
.

那么我们这里有一个标准库中没有人真正维护的错误? ( @Lukasa至少有 2 个补丁集已经开放了 1 年以上。)也许我今晚会在某个列表中提出臭味

有人(我可能会明白,不清楚)可能需要用 pdb 深入研究
或其他东西,并找出生成那些 20 字节的确切代码
阅读以便我们可以整理出一个好的错误报告。

2014 年 12 月 8 日星期一上午 9:14:09 Ian Cordasco通知@github.com
写道:

所以我们这里有一个标准库中的一个错误,没有人真的
维护? ( @Lukasa https://github.com/Lukasa 至少有 2 个补丁
已开放超过 1 年的套装。)也许我会在清单上提出臭味
今晚的某个地方


直接回复此邮件或在 GitHub 上查看
https://github.com/kennethreitz/requests/issues/2371#issuecomment -66149522
.

如果没有其他人能够做到,我会尝试在今晚或明天适应它。

那么,关于根本原因的任何消息? 是什么产生了这些短读,没有它们,情况会改善多少?

@kislyuk据我所知。 希望这个圣诞假期我有时间去追查它。

谢谢@Lukasa。 我正在处理一个性能问题,其中使用 urllib3/requests 的分块响应的下载速度比使用 curl 和其他库慢得多,并试图了解这是否是罪魁祸首。

我在这方面闲逛了一下。 短读来自 httplib 中的 _read_chunked 函数

https://fossies.org/linux/misc/Python-2.7.9.tgz/Python-2.7.9/Lib/httplib.py#l_585

2 字节读取似乎主要来自第 622 行。

我得到了与之前发布的略有不同的 strace 模式:
recvfrom(3, "400\r\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0"..., 8192, 0, NULL, NULL) = 8192
recvfrom(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0"..., 54, 0, NULL, NULL) = 54
recvfrom(3, "\r\n", 2, 0, NULL, NULL) = 2

这种模式可以解释如下:

  • self.fp.readline(第 591 行)触发 8192 字节的缓冲读取(在 socket.readline 中)
  • 消耗的每个块为 1031 字节(5 字节块长度(“400\r\n”)+ 1024 字节数据 + 2 字节终止符)
  • 我们可以从缓冲的 8192 字节中消耗 7 个这样的块,这给我们留下了 975 字节
  • 然后我们读取下一个块长度(5 个字节),剩下 970 个字节
  • 我们现在只有 970 个字节,不足以满足当前的块 (1024),因此我们返回网络以弥补 54 个字节的不足
  • 为了完成这个 httplib 对未完成的字节执行 sock.read(54) 。 socket.read 在这种情况下(具有明确的长度)将选择为指定的 54 个字节访问网络(而不是缓冲另一个 8192)
  • 然后我们开始读取 2 个字节的块终止符,这与上面的场景相同

然后模式将重复(返回步骤 1)

FWIW,我发现可以通过将 2 字节块终止符读取到块体读取中来实现适度(20% 左右)的加速,即而不是这样:

            value.append(self._safe_read(chunk_left)) 
            amt -= chunk_left

        self._safe_read(2)  # toss the CRLF at the end of the chunk

这样做:

            value.append(self._safe_read(chunk_left + 2)[:-2]) 
            amt -= chunk_left

不过,实际上,如果 54 字节的读取可以缓冲比 54(即 8192 字节)更多的字节,这可能会更好,这意味着在读取 2 字节时,缓冲的套接字不会为空。

更进一步。 我不确定小读取是吞吐量损失的主要因素(或不是在本地主机上)。 我玩弄了套接字缓冲区大小,使其成为 1031 字节的倍数,尽管 strace 不再有少量读取,但它对吞吐量没有太大影响。

我认为吞吐量的损失可能更多地与 socket.py 处理小读取的方式有关。 这是相关代码(来自socket.read):

https://fossies.org/linux/misc/Python-2.7.9.tgz/Python-2.7.9/Lib/socket.py#l_336

当您将显式长度传递给 socket.read 并且可以从现有的缓冲数据中实现时,这是代码路径:

        buf = self._rbuf
        buf.seek(0, 2)  # seek end

        #.....

        # Read until size bytes or EOF seen, whichever comes first
        buf_len = buf.tell()
        if buf_len >= size:
            # Already have size bytes in our buffer?  Extract and return.
            buf.seek(0)
            rv = buf.read(size)
            self._rbuf = StringIO()
            self._rbuf.write(buf.read())
            return rv

我在这里看到的问题是,即使是 2 字节的读取也意味着将未读的剩余部分复制到新的 StringIO 中。 这看起来对于很多小读来说会变得非常昂贵。 如果给定的 StringIO 可以在每次读取时以某种方式耗尽,而不是将未读剩余部分复制到新 StringIO 的当前模式,那么我希望这可能有助于提高吞吐量

@gardenia我还没有机会吸收所有这些,但非常感谢您在这里的努力和工作。 @shazow也许你会发现@gardenia的研究很有趣。

:+1: 谢谢@gardenia。 顺便说一下,我自己在用例中对性能的研究发现,在我的情况下,响应没有分块,但 urllib3 的执行速度比请求快 20% 以上,因此引入了一些我想描述的开销。 仍然符合这个问题的标题,但不同的根本原因。

引人入胜,感谢分享! :)

对于@Lukasa的 Hyper 来说,这似乎也是一个伟大的目标。

@alex - 我对你提到的 urllib3 vs requests non-chunked 性能问题玩了一些。 我想我看到了类似的 20% 的请求下降。

在请求中,我推测性地尝试用 stream() 的内联实现(来自 urllib3)替换对 self.raw.stream 的调用。 至少在我的机器上,它似乎使请求和 urllib3 之间的吞吐量更接近:

--- requests.repo/requests/models.py    2015-03-06 16:05:52.072509869 +0000
+++ requests/models.py  2015-03-07 20:49:25.618007438 +0000
@@ -19,6 +19,7 @@
 from .packages.urllib3.fields import RequestField
 from .packages.urllib3.filepost import encode_multipart_formdata
 from .packages.urllib3.util import parse_url
+from .packages.urllib3.util.response import is_fp_closed
 from .packages.urllib3.exceptions import (
     DecodeError, ReadTimeoutError, ProtocolError, LocationParseError)
 from .exceptions import (
@@ -652,8 +654,12 @@
             try:
                 # Special case for urllib3.
                 try:
-                    for chunk in self.raw.stream(chunk_size, decode_content=True):
-                        yield chunk
+                    while not is_fp_closed(self.raw._fp):
+                        data = self.read(amt=chunk_size, decode_content=True)
+
+                        if data:
+                            yield data
+
                 except ProtocolError as e:
                     raise ChunkedEncodingError(e)
                 except DecodeError as e:

也许您可以在您的机器上尝试相同的方法,看看它是否对您也有影响。

(注意是的,我知道对 is_fp_closed 的调用是封装破坏,它并不意味着作为一个严重的补丁只是一个数据点)

@shazow我希望 hyper 使用的BufferedSocket应该通过基本上防止小读来解决很多低效率问题。 我想知道 Py3 上的httplib是否有这个问题,因为它广泛使用io.BufferedReader ,它应该提供与BufferedSocket大致相同的好处。

当然,然而,当hyper增长到足够有用的 HTTP/1.1 功能时,我们应该尝试将它与这些其他实现一起进行基准测试,并努力使hyper尽可能快。

将近一年不活动。 关闭。

我看到了类似的问题,与urllib3相比,使用requests吞吐量减少了 10 倍

我认为这个问题存在于 urllib3 的HTTPResponse类中,当它作为迭代器被读取时,它的吞吐量非常糟糕。 我让我的代码使用了一个非常丑陋的 hack:我返回了 urllib3 使用的带下划线的httplib.HTTPResponse对象,这似乎解决了我的吞吐量问题。

有趣的事实:urllib3 的HTTPResponse超类是io.IOBase 。 Python3 的httplib.HTTPResponse超类是io.BufferedIOBase 。 我想知道它是否与此有关。

此页面是否有帮助?
0 / 5 - 0 等级