9

我使用优秀的python请求库为rest API编写了一个非常简单的客户端。一切都很好,我通过负载均衡器运行客户端,它可以正常检测空闲的 tcp 连接并杀死它们。我希望我的客户使用一些不同的 tcp 保持活动选项,而不是我的平台(linux)上的默认值。但是我没有看到任何简单的方法来告诉套接字库我想为新套接字选择一些默认选项。

当直接使用 socket.create_connection 时,使用装饰器很容易做到这一点,但我不知道当实际调用被埋在某些第 3 方库中时,我如何使装饰调用可用,就像请求的情况一样。

提前致谢

4

4 回答 4

12

较新的版本urllib3(自 1.8.3 起,于 2014-06-23 发布)支持设置套接字选项。

requests您可以通过创建自定义适配器从(自 2.4.0 起,于 2014-08-29 发布)设置这些选项:

class HTTPAdapterWithSocketOptions(requests.adapters.HTTPAdapter):
    def __init__(self, *args, **kwargs):
        self.socket_options = kwargs.pop("socket_options", None)
        super(HTTPAdapterWithSocketOptions, self).__init__(*args, **kwargs)

    def init_poolmanager(self, *args, **kwargs):
        if self.socket_options is not None:
            kwargs["socket_options"] = self.socket_options
        super(HTTPAdapterWithSocketOptions, self).init_poolmanager(*args, **kwargs)

然后您可以将此适配器安装到需要自定义套接字选项的会话(例如 setting SO_KEEPALIVE):

adapter = HTTPAdapterWithSocketOptions(socket_options=[(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)])
s = requests.session()
s.mount("http://", adapter)
s.mount("https://", adapter)
于 2016-02-08T20:28:37.190 回答
6

requestsuses urllib3,它使用标准库的http.client(或httplib,对于 2.x)调用socket.create_connection,所有这些都没有任何地方可以挂钩。

因此,您要么必须分叉其中一个库,要么即时对其进行猴子补丁。

最简单的地方可能是 in http.client.connect,因为这是一个简单的包装器socket.create_connection,可以很容易地换出:

orig_connect = http.client.HTTPConnection.connect
def monkey_connect(self):
    orig_connect(self)
    self.sock.setsockopt(…)
http.client.HTTPConnection.connect = monkey_connect

如果您使用的是 2.x,它可能就像使用httplib而不是http.client上面一样简单,但您可能想要验证这一点。

于 2013-03-01T00:27:59.583 回答
1

另一个可用的替代方法是requests_toolbelt使用 TCPKeepAliveAdapter

所见背后是设置请求 HTTPAdapter 的套接字,并考虑到您的 OSX 特性。

https://toolbelt.readthedocs.io/en/latest/adapters.html#tcpkeepaliveadapter

import requests
from requests_toolbelt.adapters.socket_options import TCPKeepAliveAdapter

session = requests.Session()
keep_alive = TCPKeepAliveAdapter(idle=120, count=20, interval=30)
session.mount('https://region-a.geo-1.compute.hpcloudsvc.com', keep_alive)
session.post('https://region-a.geo-1.compute.hpcloudsvc.com/v2/1234abcdef/servers',
             # ...
           )
于 2021-08-06T15:43:16.723 回答
0

所有浏览器(如 FireFox、Chrome、Edge 或 Safari)都将使用非常频繁的 TCP keepalives 来确保已建立的 TCP 连接保持建立,如果连接断开,它们会重新连接。在已建立的 TCP 连接上,有三个可配置的属性确定 keepalive 的工作方式。在 Linux 上,它们是:

  1. tcp_keepalive_time(默认 7200 秒)
  2. tcp_keepalive_probes(默认 9)
  3. tcp_keepalive_intvl(默认 75 秒)

Python 请求永远不会在套接字上启用 TCP keepalive(在 Linux 上,默认情况下 TCP keepalive 未在套接字上启用,应用程序必须启用它)。Python 请求使用每个操作系统上的默认套接字选项,因此对于 HTTP 1.1 持久连接,我们不知道如果连接保持空闲,是否会丢弃已建立的连接。在断开的连接上,我们只会知道下一次套接字写入发生的时间。使用低于默认值的 tcp_keepalive_time 有助于诊断断开的空闲连接。tcp_keepalive_intvl 是两个 keepalive 之间的间隔。

在下面的代码中,我们使用请求推荐的方式使用用户定义的 HTTPAdapter 通过底层 urllib3 设置套接字选项。(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 是启用keepalives,另外两个将tcp_keepalive_time和tcp_keepalive_intvl设置为10秒。

记住 TCP keepalive 依赖于平台。此代码仅适用于 Linux。

import requests, socket
from requests.adapters import HTTPAdapter

class HTTPAdapterWithSocketOptions(HTTPAdapter):
    def __init__(self, *args, **kwargs):
        self.socket_options = kwargs.pop("socket_options", None)
        super(HTTPAdapterWithSocketOptions, self).__init__(*args, **kwargs)

    def init_poolmanager(self, *args, **kwargs):
        if self.socket_options is not None:
            kwargs["socket_options"] = self.socket_options
        super(HTTPAdapterWithSocketOptions, self).init_poolmanager(*args, **kwargs)

KEEPALIVE_INTERVAL = 10
adapter = HTTPAdapterWithSocketOptions(socket_options=[(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, KEEPALIVE_INTERVAL), (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, KEEPALIVE_INTERVAL)])
s = requests.Session()
s.mount("http://", adapter)
s.mount("https://", adapter)
于 2021-05-04T03:32:21.630 回答