Cook django with aiohttp workers

There is some standart stack to run django in production. For python 2, it is nginx + gunicorn or uwsgi + monkey patching libs, such as gevent or eventlet. Generally, I prefer gunicorn + eventlet, but when you switch (or start) django project on latest python 3.5, eventlet can do some bad magic. And you can switch to a new built-in mechanism, called aiohttp. Based on gunicorn docs, you need simply switch worker to gaiohttp and that’s all.

Then, something bad happens. gaiohttp-memory

Answer is very simple: you write some memoryleaking code gaiohttp ignores max_requests option. In other words, workers won’t be killed after max_requests count. After some search on internet, there is an article from asyncio contributor about copy-n-pasting worker from aiohttp repo to gunicorn. Aiohttp docs says that you should use aiohttp.worker.GunicornWebWorker, but when you switch setting there will be error.

[5898] [ERROR] Exception in worker process
Traceback (most recent call last):
  File "./gunicorn/arbiter.py", line 557, in spawn_worker
    worker.init_process()
  File "./aiohttp/worker.py", line 37, in init_process
    super().init_process()
  File "./gunicorn/workers/base.py", line 132, in init_process
    self.run()
  File "./aiohttp/worker.py", line 40, in run
    self.loop.run_until_complete(self.wsgi.startup())
AttributeError:
    'WSGIHandler' object has no attribute 'startup'

So aiohttp.worker.GunicornWebWorker differs from standart wsgi app for django. Luckily, python community has two packages to overcome these difficulties: aiohttp-wsgi and aiodjango, add them to your requirements.txt. After this modify your wsgi.py according to docs:

import os

from django.core.wsgi import get_wsgi_application

from aiodjango import get_aio_application


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')

# Build WSGI application
# Any WSGI middleware would be added here
application = get_wsgi_application()

# Build aiohttp application
app = get_aio_application(application)

That’s all folks.

Written on November 20, 2016