【问题标题】:How to limit download speed in python3 requests?如何限制 python3 请求中的下载速度?
【发布时间】:2021-02-14 16:41:10
【问题描述】:

我正在使用请求在运行 Linux 的小型嵌入式设备上下载一个大 (~50MiB) 文件。

文件将写入附加的 MMC。

不幸的是,MMC 写入速度低于网络速度,我看到内存消耗增加,在某些情况下,我什至遇到内核“无法处理页面...”错误。

设备只有 128MiB RAM。

我使用的代码是:

            with requests.get(URL,  stream=True) as r:
                if r.status_code != 200:
                    log.error(f'??? download returned {r.status_code}')
                    return -(offset + r.status_code)
                siz = 0
                with open(sfn, 'wb') as fo:
                    for chunk in r.iter_content(chunk_size=4096):
                        fo.write(chunk)
                        siz += len(chunk)
                return siz

如何在写入 MMC 时暂时停止服务器?

【问题讨论】:

  • 这能回答你的问题吗? Download large file in python with requests
  • @python_user:除非我遗漏了一些东西,否则那里的代码与我已经在使用的代码相同。我的问题是“写入磁盘”部分比“从网络获取”部分。显然,在这种情况下,请求(或以下内容)会不断分配内存来缓冲尚未处理的传入数据。我需要一种方法来减慢源速度(例如:延迟发送帧 ACK)

标签: python python-requests bandwidth-throttling


【解决方案1】:

如果网络服务器支持http Range field,您可以请求只下载大文件的一部分,然后逐步浏览整个文件。

查看this question,其中 James Mills 提供了以下示例代码:

from requests import get

url = "http://download.thinkbroadband.com/5MB.zip"
headers = {"Range": "bytes=0-100"}  # first 100 bytes

r = get(url, headers=headers)

由于您的问题是内存问题,您需要阻止服务器一次向您发送整个文件,因为这肯定会被您设备上的某些代码缓冲。除非您可以让请求删除它收到的部分数据,否则这将始终是一个问题。 requests 下游的额外缓冲区将无济于事。

【讨论】:

  • 不错的尝试(我赞成),但我的服务器既不支持 Range 也不支持 Restart :(
  • 这很不幸。如果您只想将文件下载到磁盘,可以考虑不使用requests,而是使用curl 之类的外部工具,您可以通过os.system(或subprocess 等)调用curl 支持带宽限制,参见. unix.stackexchange.com/questions/39218
【解决方案2】:
                if r.status_code != 200:
                    log.error(f'??? download returned {r.status_code}')
                    return -(offset + r.status_code)
                siz = 0
                with open(sfn, 'wb') as fo:
                    for chunk in r.iter_content(chunk_size=4096):
                        fo.write(chunk)
                        siz += len(chunk)
                return siz

您可以将其重写为协程

import requests

def producer(URL,temp_data,n):
    with requests.get(URL,  stream=True) as r:
        if r.status_code != 200:
            log.error(f'??? download returned {r.status_code}')
            return -(offset + r.status_code)
        for chunk in r.iter_content(chunk_size=n):
            temp_data.append(chunk)
            yield #waiting to finish the consumer
            

def consumer(temp_data,fname):
    with open(fname, 'wb') as fo:
        while True:
            while len(temp_data) > 0:
                for data in temp_data:
                    fo.write(data)
                    temp_data.remove(data) # To remove it from the list
                    # You can add sleep here
                    yield #waiting for more data


def coordinator(URL,fname,n=4096):
    temp_data = list()
    c = consumer(temp_data,fname)
    p = producer(URL,temp_data,n)
    while True:
        try:
            #getting data
            next(p)
        except StopIteration:
            break
        finally:
            #writing data
            next(c)

这些都是您需要的功能。 调用它

URL = "URL"
fname = 'filename'
coordinator(URL,fname)

【讨论】:

  • 谢谢。我一定会尝试这个,但我不明白它会如何减慢进程。我的写操作已经太慢了,怎么才能让它们更慢呢?显然请求将不会等待我轮询 r.iter_content() 来填充它。我需要在请求代码中的某个地方插入一个“睡眠呼叫”(IFF 我猜对了)。
  • 是的,您可以在 for 循环中调用 next(obj) 之前添加 sleep 调用。稍等片刻,我正在重新写它
  • @ZioByte 搞定了现在可以使用了,也可以解决你的内存问题,因为它是一个一个的写块
  • @e3n 但是如果你 sleep() ,操作系统不会只是在内存中的某个地方缓冲接收到的数据包,保持内存问题吗?
  • @Anonymous1847 我对文件处理没有太多经验。但是在这里我们产生chunk 并手动使用它,所以我认为不应该有任何与内存相关的问题。睡眠是可选的。我不推荐它,但也许@Ziobyte 设备很慢所以他可能需要它
【解决方案3】:

您可以尝试使用以下 bash 命令减小 TCP 接收缓冲区的大小:

echo 'net.core.rmem_max=1000000' >> /etc/sysctl.conf

(1 MB,你可以调整这个)

这将阻止在此过程的这个阶段建立巨大的缓冲区。

然后编写代码以仅从 TCP 堆栈读取并以指定的时间间隔写入 MMC,以防止缓冲区在系统的其他地方建立,例如 MMC 写入缓冲区——例如 @e3n 的答案。

希望这会导致数据包被丢弃,然后在缓冲区再次打开后由服务器重新发送。

【讨论】:

  • 嗯...有趣,我目前的价值(根本没有/etc/sysctl.conf)非常低:net.core.rmem_max = 180224。我想在基本 TCP 之上和我的应用程序级别之下的某处正在做缓冲。
  • @ZioByte 这可能是 python 库正在缓冲一些东西。也许有一个减少缓冲区大小的选项?或者你可以尝试不同的库。
猜你喜欢
  • 2013-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-03
  • 2015-06-09
  • 1970-01-01
  • 2013-03-30
  • 2014-01-15
相关资源
最近更新 更多