Python 3请求如何强制为每个请求使用新连接?

Ξέν*_*νος 10 python python-3.x python-requests

我使用请求和线程编写了一个可暂停的多线程下载器,但是下载在恢复后无法完成,长话短说,由于特殊的网络条件,连接通常会在需要刷新连接的下载过程中终止。

您可以在我之前的问题中查看代码:

Python多连接下载器暂停后恢复使下载无休止地运行

我观察到下载恢复后可以超过 100% 并且不会停止(至少我没有看到它们停止),mmap 索引将超出范围并出现大量错误消息......

我终于发现这是因为先前请求的幽灵,导致服务器错误地从上次连接发送了未下载的额外数据。

这是我的解决方案:

  • 创建一个新连接
s = requests.session()
r = s.get(
    url, headers={'connection': 'close', 'range': 'bytes={0}-{1}'.format(start, end)}, stream=True)
Run Code Online (Sandbox Code Playgroud)
  • 中断连接
r.close()
s.close()
del r
del s
Run Code Online (Sandbox Code Playgroud)

在我的测试中,我发现requests有两个名为session的属性,一个是Titlecase,一个是小写,小写的是一个函数,另一个是一个类构造函数,它们都创建了一个requests.sessions.Session对象,有没有他们之间的区别?

如何将 keep-alive 设置为 False?

这里找到的方法不再有效:

In [39]: s = requests.session()
    ...: s.config['keep_alive'] = False
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-39-497f569a91ba> in <module>
      1 s = requests.session()
----> 2 s.config['keep_alive'] = False

AttributeError: 'Session' object has no attribute 'config'
Run Code Online (Sandbox Code Playgroud)

这里的方法不会抛出错误:

s = requests.session()
s.keep_alive = False
Run Code Online (Sandbox Code Playgroud)

但我严重怀疑它根本没有任何效果,它只是向对象添加了一个新的布尔值,而且我不认为它是由对象处理的。

我在 requests.models.Response 中看到了 close 方法,在这种情况下它有什么作用吗?或者我可以直接关闭会话吗?

最后,使用这种方法,是否可以保证服务器永远不会从以前的死连接发送额外的字节?

bru*_*off 1

我猜你的问题与服务器无关。服务器可能运行正常,问题出在线程上。

考虑到相关问题中的代码,如果它是最新的,则何时PAUSE设置为 true,这发生在第一个 argv 参数设置为 时的 50% 时间内1,每秒创建数十个线程(实际上是num_connections线程,(pressed - lastpressed).total_seconds() > 0.5并且self.paused = not self.paused逻辑每秒启动一个新批次)。在 Linux 中,您可以使用tp -H -p $pidwatch ps -T -p $pid或来检查这watch ls /proc/$pid/task/一点 - 您可能使用的是 Windows,并且有 Windows 方法来检查这一点。

单独考虑时,每批连接都是正确的,连接范围在标头上正确设置。通过闻闻自己,您会发现它们很好。当新批次的线程到达执行相同的工作时,问题就会出现。您会得到很多线程以不同的批次下载相似的范围,为您提供相同的数据。由于您的编写逻辑是相对的而不是绝对的,如果两个线程给您相同的第 123 个块,您self.position += len(chunk)将增加两个相似的块,这可能是您获得超过 100% 的原因。

要测试我所说的情况是否会发生,只需尝试下载一个不断增加的文件,然后检查您保存的文件是否不会受到这种双重增加的影响:

0000000000 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03   ................
0000000010 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00 07   ................
0000000020 00 00 00 08 00 00 00 09 00 00 00 0a 00 00 00 0b   ................
0000000030 00 00 00 0c 00 00 00 0d 00 00 00 0e 00 00 00 0f   ................
Run Code Online (Sandbox Code Playgroud)

或者通过执行类似以下操作来自己模拟一个文件范围服务器:

#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import time

hostname = "localhost"
serverport = 8081
filesizemegabytes = 8#.25
filesizebytes = int(filesizemegabytes*1024*1024)
filesizebytestr = str(filesizebytes)

class Server(BaseHTTPRequestHandler):
    def do_GET(self):
        self.do(True)
    def do_HEAD(self):
        self.do(False)
    def do(self,writebody=True):
        rangestr = self.headers.get('range')
        if type(rangestr) is str and rangestr.startswith('bytes='):
            self.send_response(206)
            rangestr = rangestr[6:]
            rangeint = tuple(int(i) for i in rangestr.split('-'))
            self.send_header('Content-Range', 'bytes '+rangestr+'/'+filesizebytestr)
        else:
            self.send_response(200)
            rangeint = (0,filesizebytes)
        self.send_header('Content-type', 'application/octet-stream')
        self.send_header('Accept-Ranges', 'bytes')
        self.send_header('Content-Length', rangeint[1]-rangeint[0])
        self.end_headers()
        if writebody:
            for i in range(rangeint[0],rangeint[1]):
                self.wfile.write(i.to_bytes(4, byteorder='big'))

if __name__ == '__main__':
    serverinstance = HTTPServer((hostname, serverport), server)
    print("Server started http://%s:%s" % (hostname, serverport))
    try:
        serverinstance.serve_forever()
    except KeyboardInterrupt:
        pass
    serverinstance.server_close()
Run Code Online (Sandbox Code Playgroud)

关于资源使用的注意事项

您不需要多线程来进行多重下载。“绿色”线程就足够了,因为你不需要多个 CPU,你只需要等待 IO。而不是multithread+ requests,更合适的解决方案是asyncio+ aiohttpaiohttp曾经requests不是很好地设计async,尽管你会在野外找到一些适应)。

最后,keep-alives当您计划再次重新连接时(这似乎正是您的情况),这很有用。您的源IP和原始IP:端口是否相同?您正在尝试强制close连接,但是一旦您意识到问题不在于服务器,请重新分析您的情况,看看是否最好进行keep-alive连接。