【问题标题】:Change the connection pool size for Python's "requests" module when in Threading在线程中更改 Python 的“请求”模块的连接池大小
【发布时间】:2013-08-27 12:55:44
【问题描述】:

(编辑:也许我对这个错误的含义有误。这是否表明我的 CLIENT 的连接池已满?或者 SERVER 的连接池已满,这是我的客户端出现的错误?)

我正在尝试使用 python threadingrequests 模块同时发出大量 http 请求。我在日志中看到此错误:

WARNING:requests.packages.urllib3.connectionpool:HttpConnectionPool is full, discarding connection:

如何增加请求的连接池大小?

【问题讨论】:

    标签: python multithreading python-requests request connection-pooling


    【解决方案1】:

    这应该可以解决问题:

    import requests.adapters
    
    session = requests.Session()
    adapter = requests.adapters.HTTPAdapter(pool_connections=100, pool_maxsize=100)
    session.mount('http://', adapter)
    response = session.get("/mypage")
    

    【讨论】:

    • 这对我有用。它应该被标记为正确答案。
    • http 替换为https 后有效。我也认为pool_connections 是不必要的。
    • 是每个会话都有自己的连接池还是多个会话共享一个连接池?
    • 请注意,(pool_connections=100, pool_maxsize=100) 是非常高的值。您应该根据您的实际情况定制它们,考虑到您连接到的不同主机的数量以及您正在使用的工作线程数。
    • @JohnStrood:一种更合规的方法,不依赖任何“私有”属性,检查给定 URL 的方法是sess.get_adapter(url).poolmanager.connection_pool_kw['maxsize']
    【解决方案2】:

    注意:仅当您无法控制连接池的构造时才使用此解决方案(如@Jahaja 的回答中所述)。

    问题是urllib3 按需创建池。它调用不带参数的urllib3.connectionpool.HTTPConnectionPool 类的构造函数。这些课程在urllib3 .poolmanager.pool_classes_by_scheme 中注册。诀窍是用具有不同默认参数的类替换这些类:

    def patch_http_connection_pool(**constructor_kwargs):
        """
        This allows to override the default parameters of the 
        HTTPConnectionPool constructor.
        For example, to increase the poolsize to fix problems 
        with "HttpConnectionPool is full, discarding connection"
        call this function with maxsize=16 (or whatever size 
        you want to give to the connection pool)
        """
        from urllib3 import connectionpool, poolmanager
    
        class MyHTTPConnectionPool(connectionpool.HTTPConnectionPool):
            def __init__(self, *args,**kwargs):
                kwargs.update(constructor_kwargs)
                super(MyHTTPConnectionPool, self).__init__(*args,**kwargs)
        poolmanager.pool_classes_by_scheme['http'] = MyHTTPConnectionPool
    

    然后你可以调用来设置新的默认参数。确保在建立任何连接之前调用它。

    patch_http_connection_pool(maxsize=16)
    

    如果您使用 https 连接,您可以创建类似的功能:

    def patch_https_connection_pool(**constructor_kwargs):
        """
        This allows to override the default parameters of the
        HTTPConnectionPool constructor.
        For example, to increase the poolsize to fix problems
        with "HttpSConnectionPool is full, discarding connection"
        call this function with maxsize=16 (or whatever size
        you want to give to the connection pool)
        """
        from urllib3 import connectionpool, poolmanager
    
        class MyHTTPSConnectionPool(connectionpool.HTTPSConnectionPool):
            def __init__(self, *args,**kwargs):
                kwargs.update(constructor_kwargs)
                super(MyHTTPSConnectionPool, self).__init__(*args,**kwargs)
        poolmanager.pool_classes_by_scheme['https'] = MyHTTPSConnectionPool
    

    【讨论】:

    • Requests 有一个内置的 API 用于提供 ConnectionPool 构造函数参数,不需要修补构造函数。 (见@Jahaja 的回答。)
    • 这取决于上下文。如果您可以控制创建 HTTPAdapter,则使用构造函数是正确的解决方案。但是在某些情况下,连接池被初始化在某个框架或库中的某个地方。在这些情况下,您可以像我上面描述的那样修补库或修补连接池构造函数。
    • 我在我的解决方案中添加了一个说明。
    • 是的,它可能是另一个问题的答案,但这是我在搜索类似以下内容时发现的问题:HttpConnectionPool is full, discarding connection python。但是该解决方案对我没有帮助,因为我的连接池是由某个库(在我的情况下是 pyes)创建的。
    • @shazow,首先 ConnectionPool 只是一个基类,你唯一能做的就是继承它,但不能传递 pool_maxsize 或任何其他(只有主机和端口)。其次,最初的问题是针对 requests/urllib3 库的,因为它是处理 HTTP 的最佳 pythonic 解决方案,所以我没有看到任何禁止在这些库的上下文中专门回答/跨度>
    【解决方案3】:

    Jahaja's answer 已经为您的问题提供了recommended solution,但它没有回答正在发生的事情,或者,如您所问,此错误意味着什么

    关于这方面的一些非常详细的信息在urllib3 official documentation 中,包requests 在后台使用来实际执行其请求。以下是您问题的相关部分,添加一些我自己的注释并省略代码示例,因为requests 具有不同的 API:

    PoolManager 类会根据需要自动为每个主机创建 ConnectionPool 实例。默认情况下,最多保留10个ConnectionPool实例[注意:requests.adapters.HTTPAdapter()中的pool_connections,默认值为10]。如果您向许多不同的主机发出请求,增加此数量可能会提高性能

    但是,请记住,这确实会增加内存和套接字消耗。

    同样,ConnectionPool 类保留了一个单独的HTTPConnection 实例池。这些连接在单个请求期间使用,并在请求完成时返回到池中。默认情况下,只会保存一个连接以供重复使用[注意:HTTPAdapter() 中的 pool_maxsize,并且 requests 将默认值从 1 更改为 10]。如果您同时向同一主机发出许多请求,则增加此数量可能会提高性能

    ConnectionPool 的池行为与 PoolManager 不同。默认情况下,如果发出新请求并且池中没有空闲连接,则将创建一个新连接。但是,如果存在多个maxsize 连接,则不会保存此连接。这意味着 maxsize 不会确定可以打开到特定主机的最大连接数,只是要保留在池中的最大连接数。但是,如果您指定 block=True [注意:在 HTTPAdapter() 中以 pool_block 的形式提供],那么最多可以打开到特定主机的 maxsize 连接 p>

    鉴于此,您的情况如下:

    • 提及的所有池都是 CLIENT 池。您(或requests)无法控制任何服务器连接池
    • 该警告与HttpConnectionPool 相关,即与同一主机 建立的同时连接数,因此您可以增加pool_maxsize 以匹配您正在使用的工作线程/线程数摆脱警告。
    • 请注意,requests 已经打开了您要求的尽可能多的同时连接,不管pool_maxsize。如果你有 100 个线程,它将打开 100 个连接。但使用默认值时,池中只会保留 10 个以供以后重用,完成请求后将丢弃 90 个。
    • 因此,较大的pool_maxsize 通过重用连接而不是通过增加并发来提高单个主机的性能。
    • 如果您正在处理多个主机,那么您可以改为更改pool_connections。默认值已经是 10,因此如果您的所有请求都针对同一目标主机,增加它不会对性能产生任何影响(但会增加使用的资源,如上述文档中所述)

    【讨论】:

      【解决方案4】:

      如果有人需要用 Python Zeep 来做这件事,并且想花一点时间来弄清楚 这是一个快速的食谱:

      from zeep import Client
      from requests import adapters as request_adapters
      
      soap = "http://example.com/BLA/sdwl.wsdl"
      wsdl_path = "http://example.com/PATH/TO_WSLD?wsdl"
      bind = "Binding"
      client = Client(wsdl_path)  # Create Client
      
      # switch adapter
      session = client.transport.session
      adapter = request_adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)
      # mount adapter
      session.mount('https://', adapter)
      binding = '{%s}%s' % (soap, bind)
      
      # Create Service
      service = client.create_service(binding, wsdl_path.split('?')[0])
      

      基本上应该在创建服务之前创建连接

      答案实际上取自一个已关闭问题的 python-zeep Repo, 作为参考,我将添加它-> here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-03
        • 1970-01-01
        • 1970-01-01
        • 2014-06-24
        • 2014-10-13
        • 1970-01-01
        • 1970-01-01
        • 2014-09-12
        相关资源
        最近更新 更多