进程内存增长巨大-Tornado CurlAsyncHTTPClient

时间:2014-05-22 08:51:25

标签: python-2.7 tornado

我正在使用Tornado CurlAsyncHTTPClient。当我为每个请求实例化相应的httpclients时,我的进程内存对于阻塞和非阻塞请求都在不断增长。如果我只有一个httpclients实例(tornado.httpclient.HTTPClient / tornado.httpclient.AsyncHTTPClient)并重用它们,则不会发生这种内存使用增长。

此外,如果我使用SimpleAsyncHTTPClient而不是CurlAsyncHTTPClient,则无论我如何实例化,都不会发生这种内存增长。

以下是重现此内容的示例代码

import tornado.httpclient
import json
import functools

instantiate_once = False
tornado.httpclient.AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient')

hc, io_loop, async_hc = None, None, None
if instantiate_once:
    hc = tornado.httpclient.HTTPClient()
    io_loop = tornado.ioloop.IOLoop()
    async_hc = tornado.httpclient.AsyncHTTPClient(io_loop=io_loop)

def fire_sync_request():
    global count
    if instantiate_once:
        global hc
    if not instantiate_once:
        hc = tornado.httpclient.HTTPClient()
    url = '<Please try with a url>'
    try:
        resp = hc.fetch(url)
    except (Exception,tornado.httpclient.HTTPError) as e:
        print str(e)
    if not instantiate_once:
        hc.close()

def fire_async_requests():
    #generic response callback fn
    def response_callback(response):
        response_callback_info['response_count'] += 1
        if response_callback_info['response_count'] >= request_count:
            io_loop.stop()
    if instantiate_once:
        global io_loop, async_hc
    if not instantiate_once:
        io_loop = tornado.ioloop.IOLoop()
    requests = ['<Please add ur url to try>']*5
    response_callback_info = {'response_count': 0}
    request_count = len(requests)
    global count
    count +=request_count
    hcs=[]
    for url in requests:
        kwargs ={}
        kwargs['method'] = 'GET'
       if not instantiate_once:
            async_hc = tornado.httpclient.AsyncHTTPClient(io_loop=io_loop)
        async_hc.fetch(url, callback=functools.partial(response_callback), **kwargs)
        if not instantiate_once:
        hcs.append(async_hc)

    io_loop.start()
    for hc in hcs:
        hc.close()
    if not instantiate_once:
        io_loop.close()

if __name__ == '__main__':
    import sys
    if sys.argv[1] == 'sync':
        while True:
            output = fire_sync_request()
    elif sys.argv[1] == 'async':
        while True:
            output = fire_async_requests()

这里将instantiate_once变量设置为True,然后执行 python check.py sync或python check.py async。进程内存不断增加

使用instantiate_once = False时,不会发生这种情况。

此外,如果我使用SimpleAsyncHTTPClient而不是CurlAsyncHTTPClient,则不会发生这种内存增长。

我有python 2.7 / tornado 2.3.2 / pycurl(libcurl / 7.26.0 GnuTLS / 2.12.20 zlib / 1.2.7 libidn / 1.25 libssh2 / 1.4.2 librtmp / 2.3)

我可以用最新的龙卷风3.2重现同样的问题

请帮助我理解这种行为并找出使用龙卷风作为http库的正确方法。

2 个答案:

答案 0 :(得分:1)

HTTPClient和AsyncHTTPClient旨在重用,因此不再一直重新创建它们总是更有效率。事实上,AsyncHTTPClient将尝试神奇地检测同一个IOLoop上是否存在现有的AsyncHTTPClient,并使用它而不是创建一个新的。

但是,即使重用一个http客户端对象更好,但它不应该泄漏以创建其中的许多对象(只要您正在关闭它们) )。这看起来像是pycurl中的一个错误:https://github.com/pycurl/pycurl/issues/182

答案 1 :(得分:0)

使用pycurl 7.19.5和这个hack来避免内存泄漏:

您的Tornado主文件:

tornado.httpclient.AsyncHTTPClient.configure("curl_httpclient_leaks_patched.CurlAsyncHTTPClientEx")

<强> curl_httpclient_leaks_patched.py

from tornado import curl_httpclient

class CurlAsyncHTTPClientEx(curl_httpclient.CurlAsyncHTTPClient):

    def close(self):
        super(CurlAsyncHTTPClientEx, self).close()
        del self._multi