2017-07-11 8 views
3

Ich habe ein Speicherleck in einer Tornado-Web-App entdeckt und bin mir nicht sicher, wie ich es beheben kann. Nach einigen Speicherprofilen habe ich festgestellt, dass mein Memcached-Client beim Schließen von Client-Verbindungen Diks verliert. Ich öffne/schließe memcached Klienten viel (einmal pro Minute, um spezifisch zu sein) als Teil einer auto-discovery mechanism w/ AWS ElastiCache.Python 3 asyncio Client-Verbindung ordnungsgemäß schließen

Hier ist ein minimal reproducer pympler mit dem Leck zu demonstrieren:

from pympler import muppy, summary 
import asyncio 
import aiomcache 

loop = asyncio.get_event_loop() 

async def hello_aiomcache(): 
    mc = aiomcache.Client("127.0.0.1", 11211, loop=loop) 
    await mc.set(b"some_key", b"Some value") 
    value = await mc.get(b"some_key") 
    print(value) 
    values = await mc.multi_get(b"some_key", b"other_key") 
    print(values) 
    await mc.delete(b"another_key") 
    mc.close() 

# establish a baseline (watch the <class 'dict line) 
summary.print_(summary.summarize(muppy.get_objects())) 

for i in range(50): 
    loop.run_until_complete(hello_aiomcache()) 

# <class 'dict grows 
summary.print_(summary.summarize(muppy.get_objects())) 

ds = [ao for ao in muppy.get_objects() if isinstance(ao, dict)] 

# leaked dict looks like {'_loop': <_UnixSelectorEventLoop running=False closed=False debug=False>, '_paused': False, '_drain_waiter': None, '_connection_lost': False, '_stream_reader': <StreamReader t=<_SelectorSocketTransport fd=34 read=polling write=<idle, bufsize=0>>>, '_stream_writer': None, '_client_connected_cb': None, '_over_ssl': False} 
ds[2364] 

Es sieht aus wie diese dict ist für immer hängen herum, bis loop.close() genannt wird. Ich bin verwirrt. I denke, Ich möchte nie die Schleife, die ich von Tornado über tornado.ioloop.IOLoop.IOLoop.current().asyncio_loop geliehen haben, zu schließen. Gibt es eine andere Möglichkeit, diese Verbindungen ordnungsgemäß zu schließen/zu bereinigen, ohne die Schleife zu schließen?

Antwort

1

Das Problem wurde verursacht durch nicht await ing mc.close().

Ich war ein wenig überrascht zu erfahren, dass eine Coroutine nicht ohne explizite Planung läuft. Ich dachte naiv, es würde irgendwann irgendwann in der Zukunft aufgerufen werden. Doch der coroutine docs ausdrücklich Zustand:

ein Koroutine Aufruf nicht seinen Code läuft starten - das Koroutine Objekt vom Aufruf zurück nichts tun, bis Sie seine Ausführung planen. Es gibt zwei grundlegende Möglichkeiten, es zu laufen beginnen: mit die ensure_future() Funktion oder die AbstractEventLoop.create_task() Methode aufrufen await coroutine oder yield from coroutine von einem anderen Koroutine oder seine Ausführung planen (vorausgesetzt, die andere Koroutine bereits läuft!).

+2

Mit [asyncios Debug-Modus] (https://docs.python.org/3/library/asyncio-dev.html#debug-mode-of-asyncio) wird 'Log-Coroutinen definiert, aber nie' von ergeben "". Das hilft, diese Art von Fehler aufzuzeigen. –