[PYTHON] Implementierung der asynchronen Verarbeitung in Django (Sellerie, Redis)

Über diesen Artikel

Beim Erstellen einer Webanwendung mit Django halte ich die asynchrone Verarbeitung für unerlässlich, und ich möchte die Einführung mit Beispielen vorstellen. Die verwendete Software ist Sellerie (Sellerie),

Umgebung

・ Python 3.6.8 ・ Django 2.2.7 ・ CentOS7

Was ist asynchrone Verarbeitung?

Eigentlich kannte ich das Wort asynchron nicht, bis ich mich tatsächlich dem Problem stellte. Oder besser gesagt, ich habe nicht einmal bemerkt, dass diese Art der Verarbeitung selbst nicht mehr die Standardeinstellung ist. Angenommen, Sie haben eine App, die einen Wert in ein Formular einfügt und den Wert über eine API oder etwas zurückgibt. kizi1.png Sie müssen sich keine Sorgen machen, da sofort reagiert wird, wenn nur ein Prozess vorhanden ist. Aber was ist, wenn Sie viel gleichzeitig verarbeiten und die Antwort 30 Minuten dauert? kizi2.png Der Benutzer muss den Browser geöffnet lassen, bis die Ergebnisse zurückgegeben werden. Wenn Sie zu einer anderen Seite wechseln oder den Browser schließen, wird der Vorgang gestoppt. Hier wird ** asynchrone Verarbeitung ** vorgestellt. Wenn Sie eine Aufgabe auslösen, deren Reaktion auf die asynchrone Verarbeitung lange dauert, wird die Verarbeitung hinter den Kulissen fortgesetzt und das Ergebnis zurückgegeben, unabhängig davon, ob der Browser geschlossen ist oder nicht.

Einführung von Sellerie / Redis

Ausführlichere Erklärungen finden Sie auf der oben vorgestellten Website. Jetzt möchte ich Celery tatsächlich auf dem Server installieren und mit Django arbeiten. Erste notwendige Elemente Die Installations- und Ausführungsumgebung ist Centos7

CentOS


pip3 install celery
pip3 install django-celery-results
yum -y install epel-release
yum search redis
yum install -y redis

Der letzte, den ich eingebe, heißt Redis (Remote-Wörterbuchserver). Es scheint, dass es für verschiedene Zwecke verwendet werden kann, wenn ich es untersuche, aber dieses Mal werde ich ihn bitten, als Makler zu arbeiten. Der Ablauf besteht darin, dass eine Aufgabe von vorne generiert wird. ⇒ Redis übergibt die Aufgabe an Sellerie. ⇒ Sellerie führt die Aufgabe aus. Übrigens scheint RabbitMQ mit Ausnahme von Redis auch als Broker verwendet werden zu können. Sie müssen sich nicht mit der Konfigurationsdatei herumschlagen, um Sellerie und Redis zu verwenden. Redis

CentOS


redis-server

Wenn Sie eingeben, wird es gestartet. Ich denke, es ist in Ordnung, je nach Anwendung damit herumzuspielen, aber ich musste nichts für meinen eigenen Gebrauch einstellen.

Zusammenarbeit zwischen Django und Sellerie

Die zum Testen erstellte Django-Projektdatei lautet wie folgt

Django


Project
├── manage.py
├── db.sqlite3
├── Tool
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── forms.py
│   ├── models.py
│   ├── templates
│   │   └── main
│   │       └── celery.html
│   ├── urls.py
│   └── views.py
└── Site
    ├── __init__.py
    ├── celery.py
    ├── settings.py
    ├── tasks.py
    ├── urls.py
    └── wsgi.py

Beschreiben Sie zunächst die Sellerieeinstellungen in settings.py.

Project/Site/settings.py


INSTALLED_APPS = [
・ ・ ・
・ ・ ・
    'django_celery_results',
]
・ ・ ・
#Sellerieeinstellungen
CELERY_BROKER_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379/1')
CELERY_RESULT_BACKEND = "django-db"

Ich habe Redis als Broker verfügbar gemacht und festgelegt, dass die Ergebnisse der Jobausführung in der Datenbank gespeichert werden. Erstellen Sie als Nächstes celery.py in derselben Hierarchie wie settings.py und schreiben Sie Folgendes.

Project/Site/celery.py



import os
from celery import Celery

#Von Sellerie verwendete Django-Konfigurationsdatei(settings.py)Angeben
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Site.settings')★

app = Celery('Site')★

#Erklärung, dass die Konfigurationsdatei von Django als Konfiguration für Sellerie verwendet werden soll. Sie können auch eine Konfigurationsdatei für Sellerie erstellen.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

★ Der Ort, den ich hinzugefügt habe, ist der, den ich für diese Zeit bearbeitet habe. Andere stammen von Official Fügen Sie außerdem Folgendes zu "__init __. Py" in derselben Hierarchie hinzu.

Project/Site/__init__.py



from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)

Jetzt können Sie loslegen. Ich verwende Sellerie-Pochen in einem tatsächlichen Projekt, aber die Einstellungen sind die gleichen wie oben. Ich bin sehr dankbar, dass ich das offizielle so verwenden kann, wie es ist.

Aufgaben- / Bildschirmerstellung

Erstellen Sie als Nächstes die Aufgabe, die Sie in Sellerie ausführen möchten. Erstellen Sie task.py in derselben Hierarchie wie settings.py und schreiben Sie dort die Verarbeitung, die Sie asynchron ausführen möchten. Ich kann mir diesmal kein gutes Beispiel vorstellen, also werde ich die Aufgabe gemäß der Formel schreiben.

Project/Site/tasks.py



from __future__ import absolute_import, unicode_literals
from celery import shared_task
import time

@shared_task
def add(x, y):
    print("wird bearbeitet")
    z = x + y
    time.sleep(10)
    print("Bearbeitung abgeschlossen")
    return z

Wichtig ist der Dekorator @ shared_task, der als Sellerie-Aufgabe erkannt wird, indem er der Funktion vorangestellt wird. Bearbeiten Sie anschließend den Bildschirm, um dies auszuführen, und die Verbindung (urls.py), um die Testumgebung einzurichten.

Erstellen Sie zunächst einen Bildschirm.

Project/Tool/templates/main/celery.html


<html lang="ja">
    <head>
	  <title>{% block title %}Sellerietest{% endblock %}</title>
	  <meta http-equiv="refresh" content="10; URL="> 
	</head>        
	<body>           
	  <h1>Auch Anfänger möchten asynchrone Verarbeitung durchführen</h1>                
			<p>Geben Sie einen Wert ein</p>
			<form method="post" action="celery">
				{% csrf_token %}
			  <tbody>
				<td><input type="text" name="input_a"></td>
				<td>+</td>
				<td><input type="text" name="input_b"></td>
			  </tbody>
			  <input type="submit" name="add_button" value="Berechnung!">
			</form>    
	  <h1>Ergebnis!</h1>
	        <tbody>        
				<td>Ausgabewert:</td>
				<td>{{ result }}</td>
			</tbody>
	</body>
</html>

kizi3.png Es ist so. Sie können es mit Bootstrap entwerfen, aber das Design ist mir diesmal egal, damit Sie es so verwenden können, wie es ist, auch wenn Sie es kopieren.

Aktualisieren Sie dann views.py.

Project/Tool/views.py


from django.shortcuts import render
from django.http import HttpResponse
from django_celery_results.models import TaskResult
from YMD_Site.tasks import add

def celery(requests):
    template = loader.get_template('main/celery.html')
    if 'add_button' in requests.POST:
        x = int(requests.POST['input_a'])
        y = int(requests.POST["input_b"])
        task_id = add.delay(x,y)★
    
    result = list(TaskResult.objects.all().values_list("result",flat=True))
    if len(result) == 0:
        resilt[0] = 0
    context = {'result': result[0]}
    return HttpResponse(template.render(context, requests))

Ich werde ein wenig erklären. Obwohl es mit einem Stern markiert ist, fügen Sie beim Ausführen einer asynchronen Verarbeitungsaufgabe (Funktion) ".delay" hinzu.

Project/Tool/views.py


    result = list(TaskResult.objects.all().values_list("result",flat=True))
    if len(result) == 0:
        resilt[0] = 0
    context = {'result': result[0]}

In Bezug auf diesen Teil wird das von Sellerie verarbeitete Ergebnis in einer dedizierten Tabelle (django_celery_results_taskresult) gespeichert. Dieses Mal versuche ich, das Ergebnis von dort zu erhalten.

Bearbeiten Sie abschließend den Verbindungsteil.

Project/Tool/urls.py


urlpatterns = [
    path('celery', views.celery),
]

Damit ist die Codebearbeitung abgeschlossen. Danach werden verschiedene Ausführungen auf dem Server durchgeführt!

Lauf

Migrieren Sie bei der Ausführung die Datenbank. Die Sellerietabelle wird automatisch durch Migration erstellt.

CentOS


[root@test1 Project]Python3 manage.py makemigrations
[root@test1 Project]Python3 manage.py migarate

OK, wenn Sie einen Sellerietisch haben

Als nächstes starten Sie Redis,

CentOS


[root@test1 Project]# redis-server
8066:C 02 Dec 15:17:51.448 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
8066:M 02 Dec 15:17:51.449 * Increased maximum number of open files to 10032 (it was originally set to 4096).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.2.12 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 8066
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

8066:M 02 Dec 15:17:51.451 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8066:M 02 Dec 15:17:51.451 # Server started, Redis version 3.2.12

OK, wenn so etwas herauskommt

Dann starten Sie Sellerie. Führen Sie den Befehl Sellerie in dem Ordner aus, in dem sich manage.py befindet.

CentOS


[root@test1 Project]# celery -A Site worker -l info
=====FINISH=====
/usr/local/lib/python3.6/site-packages/celery/platforms.py:801: RuntimeWarning: You're running the worker with superuser privileges: this is
absolutely not recommended!

Please specify a different user using the --uid option.

User information: uid=0 euid=0 gid=0 egid=0

  uid=uid, euid=euid, gid=gid, egid=egid,
 
 -------------- celery@test1 v4.3.0 (rhubarb)
---- **** ----- 
--- * ***  * -- Linux-3.10.0-957.el7.x86_64-x86_64-with-centos-7.6.1810-Core 2019-12-03 07:07:54
-- * - **** --- 
- ** ---------- [config]
- ** ---------- .> app:         Site:0x7f7a57d825f8
- ** ---------- .> transport:   redis://localhost:6379/1
- ** ---------- .> results:     
- *** --- * --- .> concurrency: 1 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----- 
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery
                

[tasks]
  . YMD_Site.tasks.add

[2019-12-03 07:07:54,586: INFO/MainProcess] Connected to redis://localhost:6379/1
[2019-12-03 07:07:54,597: INFO/MainProcess] mingle: searching for neighbors
[2019-12-03 07:07:55,625: INFO/MainProcess] mingle: all alone
[2019-12-03 07:07:55,644: WARNING/MainProcess] /usr/local/lib/python3.6/site-packages/celery/fixups/django.py:202: 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 '
[2019-12-03 07:07:55,645: INFO/MainProcess] celery@test1 ready.

Mit dem Befehl Sellerie -A Site Worker -l Info, aber mit dem letzten -l Info können Sie die Protokollstufe angeben, die ausgegeben werden soll. (FEHLER usw.) Ändern Sie den Teil "Site" des Befehls entsprechend in Ihren eigenen Projektnamen.

Führen Sie schließlich (viele von ihnen starten ...) den Server aus und versuchen Sie, die Seite aufzurufen.

CentOS


[root@test1 Project]# python3 manage.py runserver 0.0.0.0:8000

Nachdem Sie bestätigt haben, dass es erfolgreich gestartet wurde, gehen Sie zu http: // server IP address: 8000 / cellery!

kizi3.png Es wird so angezeigt.

Geben Sie einen Wert ein und drücken Sie Berechnen. kizi4.png

Dies ist der Punkt, aber wenn Sie die Taste drücken, wird die Seite neu geladen. Dieser Vorgang wird nach der Berechnung 10 Sekunden lang unterbrochen, sodass das Ergebnis nicht sofort zurückgegeben wird, die Seite jedoch nicht in den Standby-Zustand versetzt wird und auch beim Schließen des Browsers kein Problem auftritt. Nach dem Drücken der Taste können Sie sehen, dass der Server die Verarbeitung hinter den Kulissen fortsetzt. Wenn Sie eine Weile warten kizi5.png Das Ergebnis ist zurück!

Übrigens auf dem Sellerie-Bildschirm auf der Serverseite

CentOS


[2019-12-03 07:24:02,046: INFO/MainProcess] Received task: Site.tasks.add[52948e50-4803-4c11-87ab-8eab088e1d7d]  
[2019-12-03 07:24:02,053: WARNING/ForkPoolWorker-1]wird bearbeitet
[2019-12-03 07:24:12,064: WARNING/ForkPoolWorker-1]Bearbeitung abgeschlossen
[2019-12-03 07:24:12,135: INFO/ForkPoolWorker-1] Task Site.tasks.add[52948e50-4803-4c11-87ab-8eab088e1d7d] succeeded in 10.082466656996985s: 2

Sie können sehen, dass der Prozess ordnungsgemäß funktioniert. Wenn Sie den Verwaltungsbildschirm von Django aufrufen, wird automatisch eine Seite erstellt und Sie können den Verarbeitungsinhalt überprüfen. kizi6.png

Zusammenfassung

Dieses Mal war der Zweck, die asynchrone Verarbeitung zu versuchen, also habe ich den Bildschirm usw. relativ angemessen gemacht. Der HTML-Code wird so verarbeitet, dass er alle 10 Sekunden automatisch aktualisiert wird. Denn wenn Sie es manuell aktualisieren, um das Ergebnis auf dem Bildschirm wiederzugeben, wird das Formular erneut gesendet ... Es scheint, dass Sie hier Maßnahmen ergreifen können, aber diesmal ist es nicht relevant, daher habe ich es weggelassen.

Eigentlich wollte ich serielle Verarbeitung, parallele Verarbeitung und Mehrfachstart von Arbeitern einbeziehen, aber es ist schon ziemlich lang geworden, deshalb werde ich es in einem anderen Artikel schreiben. Es gibt auch viele Probleme, die bei der Verwendung gelöst werden müssen, also hoffe ich, dass ich auch darüber schreiben kann. Vielen Dank an alle, die bisher gelesen haben.

Recommended Posts

Implementierung der asynchronen Verarbeitung in Django (Sellerie, Redis)
Sellerie asynchrone Verarbeitung in Flask
Asynchrone Verarbeitung (Threading) in Python
Asynchrone Verarbeitung mit LINE BOT: RQ (Redis Queue) in Python
Implementierung der Login-Funktion in Django
Implementierung der Like-Schaltfläche in Django + Ajax
Ablauf des Ergebnisses der asynchronen Verarbeitung mit Django und Sellerie
Asynchrone Verarbeitung mit Linebot in der Jobwarteschlange
Asynchrone Verarbeitung in Python: Asyncio-Reverse-Referenz
Modell in Django
Form in Django
RNN-Implementierung in Python
Dateiverarbeitung in Python
Multithread-Verarbeitung in Python
ValueObject-Implementierung in Python
Implementierung des Dropdown-Menüs in Django
Sellerie-Notizen zu Django
Textverarbeitung mit Python
Modelländerungen in Django
SVM-Implementierung in Python