Многопоточность в Django с использованием потоков и очереди

В 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) как обработку почты, будет завершена функция асинхронной отправки почты.

Оцените статью
devanswers.ru
Добавить комментарий