Django と Celery で非同期処理を実装します。
リアルタイムで重たい処理を実行したい場合、様々な問題が出てきます。パッと思いつくのは、Apache や Nginx など Web サーバーのタイムアウト問題でしょうか。あと、ユーザを待たせてしまうUXの問題などもあります。
この問題を解決する方法として、重たい処理は非同期としてバックグラウンドで実行し、HTTP のレスポンスは、すぐに返してしまう方法があります。
Contents [show]
完成イメージ
環境とバージョン
- CentOS 7.4.1708
- Python 3.6.4
- Django 2.0.3
- Celery 4.2.0
- django-celery-results 1.0.1
- mysqlclient 1.3.12
- epel-release 7.11
- Redis 3.2.10
- MySQL 5.7
インストール
Python, Django
Python と Django のインストールは、Python Web フレームワーク Django の環境を構築するの記事を参考にしてください。
Celery, django-celery-results, redis
1 | $ pip install celery django-celery-results redis |
Celery と Celery で使用する Python パッケージをインストールします。
MySQL のインストール
1 2 | $ sudo yum-config-manager --disable ius$ sudo yum install mysql mysql-devel mysql-server mysql-utilities |
Celery の結果を DB に保存するために MySQL をインストールします。
MySQL のインストールについては、こちらを参考にしてください。
Python を ius リポジトリでインストールした場合、MySQL リポジトリと衝突します。
なので、先に ius リポジトリを disable にしてください。
mysqlclient
1 | $ sudo pip install mysqlclient |
MySQL に接続するための Python パッケージをインストールします。
Redis
1 2 3 | $ sudo yum install epel-release$ sudo yum install redis$ sudo systemctl start redis |
Broker は RabbitMQ ではなく、Redis を使いたいと思います。
実装
パッケージのインストールが一通り終わりましたので実装して行きます。
tree
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ tree.├── app│ ├── __init__.py│ ├── apps.py│ ├── tasks.py│ ├── templates│ │ └── app│ │ └── index.html│ └── views.py├── manage.py└── proj ├── __init__.py ├── celery.py ├── settings.py ├── urls.py └── wsgi.py4 directories, 11 files |
まず最初に、ファイル構成です。このように実装していきます。
settings.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ALLOWD_HOSTS = ['*']INSTALLED_APPS = [ 'app.apps.AppConfig', 'django_celery_results', ...]DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'proj', 'USER': 'root', 'PASSWORD': 'Eba7|B33veK+', 'OPTIONS': { 'charset': 'utf8mb4', } }}CELERY_RESULT_BACKEND = 'django-db' |
settings.py です。
Celery の実行結果を MySQL に保存するために、django-celery-results を使います。そのため、INSTALLED_APPS と CELERY_RESULT_BACKEND の設定をします。
celery.py
1 2 3 4 5 6 7 8 | import osfrom celery import Celeryos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings')app = Celery('proj')app.config_from_object('django.conf:settings', namespace='CELERY')app.autodiscover_tasks() |
Celery の設定ファイルを実装します。この celery.py は settings.py と同じ階層に保存します。
__init__.py
1 2 3 | from .celery import app as celery_app__all__ = ('celery_app',) |
この __init__.py は celery.py と同じ階層の __init__.py です。
tasks.py
1 2 3 4 5 | from celery import shared_task@shared_taskdef add(x, y): return x + y |
tasks.py は、非同期処理のメインとなる関数を実装するファイルです。
サンプルとして、足し算結果を返す関数を実装しましたが、本来であれば、ここに重たい処理がくると思います。
urls.py
1 2 3 4 5 6 | from django.urls import pathfrom app.views import indexurlpatterns = [ path('', index, name='index'),] |
ブラウザから動作を確認するために、urls.py を用意します。
views.py
1 2 3 4 5 6 7 8 9 | from django.shortcuts import renderfrom django_celery_results.models import TaskResultfrom app.tasks import adddef index(request): add.delay(1, 1) object_list = TaskResult.objects.all().order_by('pk') context = {'object_list': object_list} return render(request, 'app/index.html', context) |
アクセスがあったら、バックグランドに足し算処理を投げ、ブラウザにすぐにレスポンスを返します。
ページに何か表示した方がいいと思ったので、Celery の結果を表示しています。
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <html> <head> <title>Django と Celery で非同期処理を実装する</title> </head> <body> <h1>Django と Celery で非同期処理を実装する</h1> <table> <thead> <tr> <th>#</th> <th>Task Id</th> <th>Task Name</th> <th>Status</th> <th>Result</th> <th>Date Done</th> </tr> </thead> <tbody> {% for object in object_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ object.task_id }}</td> <td>{{ object.task_name }}</td> <td>{{ object.status }}</td> <td>{{ object.result }}</td> <td>{{ object.date_done }}</td> </tr> {% endfor %} </tbody> </table> </body></html> |
DB マイグレーション
Create database
1 | $ mysql -u root -p -e 'create database proj default charset utf8mb4' |
非同期結果を DB に保存しますので、まずデータベースを用意します。
migrate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ python3.6 manage.py migrateOperations to perform:Apply all migrations: admin, auth, contenttypes, django_celery_results, sessionsRunning migrations:Applying contenttypes.0001_initial... OKApplying auth.0001_initial... OKApplying admin.0001_initial... OKApplying admin.0002_logentry_remove_auto_add... OKApplying contenttypes.0002_remove_content_type_name... OKApplying auth.0002_alter_permission_name_max_length... OKApplying auth.0003_alter_user_email_max_length... OKApplying auth.0004_alter_user_username_opts... OKApplying auth.0005_alter_user_last_login_null... OKApplying auth.0006_require_contenttypes_0002... OKApplying auth.0007_alter_validators_add_error_messages... OKApplying auth.0008_alter_user_username_max_length... OKApplying auth.0009_alter_user_last_name_max_length... OKApplying django_celery_results.0001_initial... OKApplying sessions.0001_initial... OK |
データベースを用意したら、テーブルを作成します。
動作確認
Django の起動
1 2 3 4 5 6 7 8 | $ python3.6 manage.py runserver 0.0.0.0:8000Performing system checks...System check identified no issues (0 silenced).July 04, 2018 - 04:29:40Django version 2.0.3, using settings 'proj.settings'Starting development server at http://0.0.0.0:8000/Quit the server with CONTROL-C. |
ブラウザでアクセスするために、Django のデバッグ用サーバー runserver を起動します。
Celery の起動
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $ celery -A proj worker -l info-------------- celery@localhost.localdomain v4.2.0 (windowlicker)---- **** -------- * *** * -- Linux-3.10.0-693.21.1.el7.x86_64-x86_64-with-centos-7.4.1708-Core 2018-07-04 04:35:30-- * - **** ---- ** ---------- [config]- ** ---------- .> app: proj:0x7f42c6e217f0- ** ---------- .> transport: redis://localhost:6379/0- ** ---------- .> results:- *** --- * --- .> concurrency: 1 (prefork)-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)--- ***** ------------------- [queues].> celery exchange=celery(direct) key=celery[tasks]. app.tasks.add[2018-07-04 04:35:30,719: INFO/MainProcess] Connected to redis://localhost:6379/0[2018-07-04 04:35:30,743: INFO/MainProcess] mingle: searching for neighbors[2018-07-04 04:35:31,794: INFO/MainProcess] mingle: all alone[2018-07-04 04:35:31,813: WARNING/MainProcess] /usr/lib/python3.6/site-packages/celery/fixups/django.py:200: UserWarning: Using settings.DEBUG leads to a memory leak, never use this setting in production environments!warnings.warn('Using settings.DEBUG leads to a memory leak, never '[2018-07-04 04:35:31,813: INFO/MainProcess] celery@localhost.localdomain ready. |
非同期処理をするために、Celery を起動します。
ブラウザで http://192.168.33.10:8000 にアクセス
ブラウザでアクセスする毎に非同期処理 Celery がバックグラウンドで実行し、結果が表示されるのが確認できると思います。
まとめ
Django と Celery で非同期処理を実装しました。
今回は、足し算するだけの軽い処理なので、本当は非同期にする必要がないですね。
しかし、最初から重たい処理になることがわかっている場合や、運用後にデータ量が多くなり、処理が重たくなってきた場合は、Celery で非同期処理を検討してみるといいかもしれません。