是否可以覆盖请求中的默认套接字选项?

时间:2013-03-01 00:19:53

标签: python sockets tcp

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

当直接使用socket.create_connection时,这很容易与装饰器一起使用,但我不知道当实际调用被隐藏在某些第三方库中时,我是如何使该装饰调用可用的,如同请求。

提前致谢

4 个答案:

答案 0 :(得分:9)

较新版本的urllib3(自2014年4月23日发布的1.8.3版本)支持设置套接字选项。

您可以通过创建自定义适配器,从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)

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

adapter = HTTPAdapterWithSocketOptions(socket_options=[(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)])
s = requests.session()
s.mount("http://", adapter)
s.mount("https://", adapter)

答案 1 :(得分:3)

requests使用urllib3,它使用标准库的http.client(或httplib,用于2.x),调用socket.create_connection,所有这些都无需任何地方挂钩的东西。

所以,你要么必须分叉其中一个库,要么动态地进行monkeypatch。

最简单的地方可能在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一样简单,但您可能需要验证。

答案 2 :(得分:0)

FireFox、Chrome、Edge 或 Safari 等所有浏览器都将使用非常频繁的 TCP keepalive,以确保已建立的 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 之间的间隔。

在下面的代码中,我们使用 requests 推荐的方式使用用户定义的 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)

答案 3 :(得分:0)

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

这背后是设置请求 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',
             # ...
           )