В Django, если процесс, выполняемый при запросе, требует много времени, вы можете захотеть оставить его на потом и все равно вернуть ответ.
Например, отправка подтверждения по электронной почте, когда регистрация пользователя завершена. Если приложение, которому требуется 1 секунда для отправки электронного письма, получает 1 000 обращений в секунду, потребуются следующие вещи.
- Приложение не блокируется во время сетевого ввода-вывода для отправки почты.
- Задания по отправке почты запускаются после и выполняются в том порядке, в котором к ним обращаются.
У меня была возможность создать функцию отправки почты в Django, поэтому вот мои заметки.
Подтверждение базовой операции
Сначала мы немного изменили код примера в ссылке на очередь, чтобы проверить процесс с использованием очереди и потоков.
import threading
import queue
import time
q = queue.Queue()
def worker():
while True:
item = q.get()
print(f'Working on {item}')
time.sleep(1)
print(f'Finished {item}')
q.task_done()
# turn-on the worker thread
threading.Thread(target=worker, daemon=True).start()
# send thirty task requests to the worker
for item in range(5):
print("Put item", item)
q.put(item)
print('All task requests sentn', end='')
# block until all tasks are done
q.join()
print('All work completed')
Сначала объявите q
как экземпляр Queue
в глобальной переменной. Эта очередь будет заполнена заданиями для выполнения.
Далее мы определяем метод worker
как исполнитель задания, который получит задание, распечатает его и подождет одну секунду. Из-за while true
он всегда ждет,
Когда q.get()
вернет возвращаемое значение, основной процесс будет выполнен.
Затем запустите новый поток с помощью threading.Thread()
, передав целевой метод, который вы хотите выполнить. Затем добавьте элемент как задание в очередь, и рабочий начинает выполняться. q.join()
останавливает дальнейшую обработку, пока q.task_done()
не будет вызван для всех q
.
Outpus
Put item 0
Put item 1
Put item 2
Put item 3
Put item 4
All task requests sent
Working on 0
Finished 0
Working on 1
Finished 1
Working on 2
Finished 2
Working on 3
Finished 3
Working on 4
Finished 4
All work completed
Вывод показывает, что все добавления заданий завершаются первыми, а задания выполняются в порядке их добавления, что указывает на то, что управление заданиями и их выполнение происходит асинхронно. Похоже, что отправка электронной почты также может быть реализована таким образом. Попробуйте.
Асинхронная обработка заданий в Django
Сначала создайте API Django для тестирования.
$ django-admin startproject mysite
$ cd mysite/
$ python manage.py startapp api
Затем добавьте следующий код.
mysite/settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api',
'rest_framework'
]
...
mysite/urls.py
from django.contrib import admin
from django.urls import path
from api import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index)
]
api/views.py
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Hello, world.")
Затем запустите сервер
$ python manage.py runserver
Зайдите на http://127.0.0.1:8000/index/
и если на экране появится Hello, world.
, то вы готовы.
Теперь перейдем к созданию функции асинхронной почты.
Создайте новый файл api/mail.py
и напишите его содержимое следующим образом.
import threading
import queue
import time
q = queue.Queue()
def worker():
while True:
item = q.get()
print(f'Working on {item}')
time.sleep(3) # Do any processing here.
print(f'Finished {item}')
q.task_done()
# Five threads are set up to parallelize the process.
for _ in range(5):
threading.Thread(target=worker, daemon=True).start()
def add(item):
q.put(item)
Затем измените файл views.py
следующим образом.
from django.http import HttpResponse
from api.mail import add as mail_add
# Create your views here.
def index(request):
item = request.GET.get("param")
mail_add(item)
return HttpResponse("Hello, world.")
Перезапустите сервер в этом состоянии и попробуйте отправить серию запросов следующим образом.
curl http://127.0.0.1:8000/index/?param=1
curl http://127.0.0.1:8000/index/?param=2
curl http://127.0.0.1:8000/index/?param=3
curl http://127.0.0.1:8000/index/?param=4
curl http://127.0.0.1:8000/index/?param=5
Выходит
Working on 1
[15/May/2021 12:12:28] "GET /index/?param=1 HTTP/1.1" 200 13
Working on 2
[15/May/2021 12:12:28] "GET /index/?param=2 HTTP/1.1" 200 13
Working on 3
[15/May/2021 12:12:28] "GET /index/?param=3 HTTP/1.1" 200 13
Working on 4
[15/May/2021 12:12:28] "GET /index/?param=4 HTTP/1.1" 200 13
Working on 5
[15/May/2021 12:12:28] "GET /index/?param=5 HTTP/1.1" 200 13
Finished 1
Finished 2
Finished 3
Finished 4
Finished 5
Завершение работы будет выведено почти одновременно. Если переписать часть time.sleep(3)
как обработку почты, будет завершена функция асинхронной отправки почты.