- PVSM.RU - https://www.pvsm.ru -
Twisted [1] — асинхронный (событийно-ориентированный) фреймворк, написанный на Python. Мощное средство для быстрой разработки сетевых (и не только) сервисов. Он разработан с использованием паттерна проектирования Reactor [2]. Сервисы созданные с использованием Twisted быстры и надежны, фреймворк позволяет не писать макаронный код, насыщенный непонятными коллбэками, имеет внутри себя красивые хелперы (Deferred, Transport, Protocol etc). Другими словами, делает нашу жизнь бекенд разработчиков лучше.
Основная проблема в том, что многочисленные, надежные, оттестированные, удобные библиотеки, использующие в своей основе синхронные модули Python (socket, os, ssl, time, select, thread, subprocess, sys, signal etc), просто возьмут и заблокируют нам основной процесс, цикл реактора и наступит беда. Такими библиотеками, к примеру, являются psycopg2, request, mysql и другие. В частности, psycopg2 используется в Django ORM как один из бекендов баз данных.
Есть три пути. Сложный, приемлемый и хороший. Сложный — реализовать аналог библиотеки на Twisted. Приемлемый — использовать deferToThread и запускать синхронный код в отдельных потоках (используя пул потоков реализованный в Twisted). О хорошем пути (по моему мнению) и пойдет речь в заметке.
[3]
Я не стал писать собственную реализацию реактора с возможностью отправлять код в гринлеты, так как нашел готовое решение, протестировал и внедрил в проект. Код реактора можно забрать отсюда [4].
Для использования geventreactor при инициализации приложения нужно его установить:
from geventreactor import install
install()
Теперь нам доступны новые методы:
__all__ = ['deferToGreenletPool', 'deferToGreenlet', 'callMultipleInGreenlet', 'waitForGreenlet', 'waitForDeferred',
'blockingCallFromGreenlet', 'IReactorGreenlets', 'GeventResolver', 'GeventReactor', 'install']
По аналогии с reactor.deferToThread(f, *args, **kwargs), можно вызывать reactor.deferToGreenlet(f, *args, **kwargs), где f — callable объект, а *args и **kwargs его аргументы.
Чтобы все заработало необходимо также пропатчить библиотеки в пространстве имен:
from gevent import monkey
monkey.patch_all()
После данных манипуляций, основные библиотеки Python будут пропатчены фреймворком Gevent. Смотрите документацию по Gevent [5]
Теперь все библиотеки или код, который напрямую импортирует их, при вызове блокирующихся методов или функций, будут вызывать соответствующие события в системе Gevent. На эти события повешены коллбеки, позволяющие переключать контекст между гринлетами.
У меня в проекте используется Django ORM для манипуляции данными в PostgreSQL. Поэтому для того, чтобы методы ORM не блокировали процесс нужно использовать специальный бекенд, позволяющий создавать пул соединений с БД и переключаться между соединениями. Одним из бекендов является django-db-geventpool [6]
Использовать django-db-geventpool не трудно. Достаточно следовать документации.
Метод reactor.deferToGreenlet возвращает объект Deferred, с которым можно работать как с обычным Deferred.
Например, у нас есть модель:
class ExampleModel(models.Model):
title = models.CharField(max_length=256)
Мы хотим получить все модели и передать их какому-то обработчику внутри системы. Мы можем написать что-то вроде:
d = reactor.deferToGreenlet(ExampleModel.objects.all)
И наш код не заблокирует основной процесс. Ведь в тот момент, когда Django ORM вызовет cursor.execute(), который будет ожидать ответ от драйвера базы данных, geventreactor переключит контекст на другой Deferred.
Мы можем выполнять синхронный код внутри Twisted, не создавая при этом лишних потоков или процессов, при этом не блокируя event loop реактора. Главное следовать основным принципам работы с асинхронными системами, куски кода не должны выполняться слишком долго, gevent позволяет принудительно переключать контекст из любого места кода, там, где это нам удобно, достаточно лишь вызвать gevent.sleep().
Автор: viatoriche
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/106560
Ссылки в тексте:
[1] Twisted: https://twistedmatrix.com/trac/
[2] Reactor: https://en.wikipedia.org/wiki/Reactor_pattern
[3] Image: https://www.pvsm.ru/post/266887/
[4] отсюда: https://gist.github.com/yann2192/3394661
[5] Смотрите документацию по Gevent: http://www.gevent.org/gevent.monkey.html#module-gevent.monkey
[6] django-db-geventpool: https://pypi.python.org/pypi/django-db-geventpool
[7] Источник: http://habrahabr.ru/post/266887/
Нажмите здесь для печати.