yamakiy’s tech blog

技術系の記事をメインにやります。

django-q+supervisorで非同期処理

django-q+supervisorで非同期処理

Django Advent Calendar 2018 3日目
django-q+supervisorで非同期処理が実現できるまでをやります

今回検証用に作ったprojectはgitに上げてます
GitHub - yamakiy/djangoq_example: django-qを利用した非同期実装のメモ

環境

下準備

必要なもの

入れる時に利用したコマンド

yum install supervisor
yum install redis
pip3 install django-q
pip3 install redis

各種設定

django projectのsettings

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_q' # これを足す
]

# 今回は裏にmariadbを使ってます
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'example',
        'USER': 'example',
        'PASSWORD': 'ExamplePassw0rd',
        'HOST': 'localhost',
        'PORT': '3306'
    }
}

# django-qの設定
Q_CLUSTER = {
    'workers': 2, # 並行起動するワーカー数 デフォルトはcpu数
    'timeout': 60, # workerのタイムアウト時間 defaultはNone
    'redis': {
        'host': '127.0.0.1',
        'port': 6379,
        'db': 0,
    }
}

djangoのmigrateを実行

migrateで django_qとつけると他のmigrateが勝手に進行しません

python3.6 manage.py migrate django_q

DBの中を見るとこんな感じでテーブルができてるはず

MariaDB [example]> show tables;
+-------------------+
| Tables_in_example |
+-------------------+
| django_migrations |
| django_q_ormq     |
| django_q_schedule |
| django_q_task     |
+-------------------+

続いてsupervisorの設定

supervisorはプロセスをデーモン化して管理できる便利ツール

redisのsupervisor設定

/etc/supervisord.d/redis.ini

[program:redis]
command=/usr/bin/redis-server /etc/redis.conf
autostart=true
autorestart=true
続いてdjango-qの設定を入れる
[program:django-q]
command = python3.6 /root/django/example_site/manage.py qcluster
stopasgroup = true
autostart=true
autorestart=true

supervisor動作の確認

[root@localhost example_site]# supervisorctl status
django-q                         RUNNING   pid 16308, uptime 0:00:03
redis                            RUNNING   pid 16309, uptime 0:00:03

正常に動いてなかったらpythonのpathとかを見直す

簡単な処理をやってみる

ページを見るたびにDB内にlogを保存する処理を非同期実行する

tasks.py

import time
import datetime
from .models import MyLog

# 引数にmessageを受け取り、日付とともにログを保存する
def log_save(message):
    # わかりやすくするため、5秒スリープしてログを保存する
    time.sleep(5)
    date_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    MyLog(log_txt=f"{date_str} {message}").save()

views.py

from django_q.tasks import async_task

class TopView(TemplateView):
    template_name = "example_app/top.html"

    def dispatch(self, request, *args, **kwargs):
        # 非同期実行でユーザーエージェントをログに保存
        async_task("example_app.tasks.log_save", request.META["HTTP_USER_AGENT"])
        return super(TopView, self).dispatch(request, *args, **kwargs)

サーバーを動かして確認してみる。 f:id:yamakiy:20181124231611p:plain

MariaDB [example]> select * from example_app_mylog;
+----+-----------------------------------------------------------------------------------------------------------------------------------------+
| id | log_txt                                                                                                                                 |
+----+-----------------------------------------------------------------------------------------------------------------------------------------+
|  1 | 2018-11-24 13:48:03 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 |
+----+-----------------------------------------------------------------------------------------------------------------------------------------+

サーバーからの応答時間は5秒以内に完了している。 ログも書き込めている。

python manage.py qmonitorでクラスターの状況を確認できる f:id:yamakiy:20181125221537p:plain

項目 役割
Host クラスターが起動しているサーバーのホスト名
Id クラスターのprocess ID(pid)
State クラスターのステータス
Pool クラスタープール内のworker数
TQ 待機タスク
RQ キューの結果の数 しばらくするとクリアされる
RC プロセスの失敗、またはtimeoutによって生まれ変わったプロセス数
UP クラスタを起動してから経過した時間

python3.6 manage qinfo f:id:yamakiy:20181125225437p:plain

Taskは24時間単位の統計を示します 他、現在までのプロセス成功数や失敗数、ワーカーの数などが見れる

終わりに

今回は非同期での簡単な処理実装までをやりました。

もう少し高度な例はこちらの公式を参考にどうぞ。
Examples — Django Q 1.0.1 documentation