LoginSignup
2
5

More than 3 years have passed since last update.

Django+Herokuで簡単に非同期処理を実装したい

Posted at

Herokuに乗ったDjangoに非同期処理をさせたいよ~~

Herokuは30秒レスポンスが返らないとタイムアウトさせてしまいます.
公式ドキュメントでも以下のように時間のかかる処理は非同期で行うことを推奨(というかほぼ強制)しています.

  • メールの送信
  • リモート API へのアクセス (Twitter への投稿、Flickr の照会など)
  • Web スクレイピングまたは Web クローリング
  • 画像または PDF のレンダリング
  • 大量の計算 (フィボナッチ数列の計算など)
  • データベースの大量使用 (時間のかかるクエリまたは多数のクエリ、N+1 クエリ)

Herokuのドキュメントに沿って作ってみる

Heroku自体がPythonの非同期処理についてまとめているのでそれに沿って作ってみます.
https://devcenter.heroku.com/ja/articles/python-rq

まずworkerを作成します.

worker.py
import os

import redis
from rq import Worker, Queue, Connection

listen = ['high', 'default', 'low']

redis_url = os.environ.get('REDISTOGO_URL', 'redis://localhost:6379')
sys.path.append(os.getcwd())
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')

conn = redis.from_url(redis_url)

if __name__ == '__main__':
    with Connection(conn):
        worker = Worker(map(Queue, listen))
        worker.work()

この時,Djangoの設定を読み込みます.
setdefaultで渡すだけだとそんなモジュールないが?って怒られてしまうのでpathも追加してください.

キューを作って投げ込み動かします.
投げ込むジョブはあらかじめUtils.pyにでも外だしで書いておきましょう.

views.py
from django.views import generic
from rq import Queue
from worker import conn
from myapp.utils import myfunc

class myView(generic.View):
    context_object_name = 'myView'

    def get(self, request, *args, **kwargs):
        q = Queue(connection=conn, default_timeout=600)
        r = q.enqueue(myfunc, request.param)    
        context = self.get_context_data()
        return render(request, 'myapp/myView.html', context)

あとはQueue作って関数を投げ込むだけです.難しくないですね.

Procfile
web: gunicorn myapp.wsgi --log-file -
worker: python worker.py

Heroku向けのProcfileにworkerを追加しておきましょう.

heroku scale worker=1

Herokuにデプロイしたらscaleでworkerを増やしましょう.

Django-rqを使う方法

こちらを参考に実装します.

Heroku特有の手続きはworkerをProcfileに登録することとsettings.pyをちょっと変えることだけです.

settings.py
RQ_QUEUES = {
    'default': {
        'HOST': 'localhost',
        'PORT': 6379,
        'DB': 0,
        'PASSWORD': '',
        'DEFAULT_TIMEOUT': 7200,
        'URL': os.getenv('REDISTOGO_URL', 'redis://localhost:6379/0'), #追加
    },
}
Procfile
web: gunicorn myapp.wsgi --log-file -
worker: python manage.py rqworker

こっちの方が固有ライブラリを使っている分すっきりしますね.(settingsに設定まとまるし)

終わりに

Herokuでは好きなだけ非同期処理のworkerが増やせますが,料金もworkerの分だけメインのdynoと同じ料金でかかるので個人趣味でやる分には結構負担が大きいです.

大した処理しないのに料金倍にしてworker作るのもなぁ~~ってなっているので良い方法あったら教えてください.

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5