Это достаточно вольный перевод статьи об основных новшествах асинхронного драйвера для
mongodb
используемого в tornado
. Основной мотив, который послужил для написания этого перевода — новшества, появившиеся в этой версии, такие как поддержка asyncio
, async
, await
и Python 3.5
. Сама статья не сколько перечисление новшеств, сколько лаконичные примеры асинхронной работы с MongoDB
. Введение
asyncio
aggregate
Python 3.5
async and await
Введение
Недавно была опубликована новая
Beta
версия Python
драйвера для Mongodb
, Motor
. В этой версии содержится одно из самых больших обновлений. Для установки можно использовать:
python -m pip install --pre motor==0.5b0
Motor 0.5
по прежнему зависит от PyMongo 2.8.0
. Это устаревшая версия PyMongo
, но сейчас не было достаточно времени чтоб полностью перейти на третью версию, что простительно, так как этот релиз достаточно большой.asyncio
Motor
теперь может интегрироваться с asyncio
, как альтернатива Tornado
. Большая благодарность Реми Джолину, Андрею Светлову svetlov и Николаю Новику за их огромный вклад в интеграцию Motor
для работы с asyncio
.API-Интерфейсы
Tornado
и asyncio
являются родственными. Пример Motor
с Tornado
:# Tornado API
from tornado import gen, ioloop
from motor.motor_tornado import MotorClient
@gen.coroutine
def f():
result = yield client.db.collection.insert({'_id': 1})
print(result)
client = MotorClient()
ioloop.IOLoop.current().run_sync(f)
И здесь пример для asyncio:
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
@asyncio.coroutine
def f():
result = yield from client.db.collection.insert({'_id': 1})
print(result)
client = AsyncIOMotorClient()
asyncio.get_event_loop().run_until_complete(f())
В отличие от
Tornado
, asyncio
не включает реализацию http
, а тем более не является фреймворком. Для этого используйте библиотеку aiohttp
Андрея Светлова. Небольшой пример для работы Motor с aiohttp.aggregate
MotorCollection.aggregate теперь по умолчанию возвращает курсор, и курсор возвращается непосредственно без
yield
. Старый синтаксис больше не поддерживается:# Motor 0.4 and older, no longer supported.
cursor = yield collection.aggregate(pipeline, cursor={})
while (yield cursor.fetch_next):
doc = cursor.next_object()
print(doc)
В
Motor 0.5
просто сделайте:# Motor 0.5: no "cursor={}", no "yield".
cursor = collection.aggregate(pipeline)
while (yield cursor.fetch_next):
doc = cursor.next_object()
print(doc)
В
asyncio
для этого используется yield from
:# Motor 0.5 with asyncio.
cursor = collection.aggregate(pipeline)
while (yield from cursor.fetch_next):
doc = cursor.next_object()
print(doc)
Python 3.5
Сейчас
Motor
совместим с Python 3.5
, что потребовало определённых усилий. Это было трудно, потому что Motor
не просто работает с сопрограммами (coroutines), он использует сопрограммы внутри себя для реализации некоторых из своих функций, таких как MotorClient.open и MotorGridFS.put.Был метод для написания сопрограмм, которые работают в
Python 2.6
c Python 3.4
, но в Python 3.5
это было окончательно поломано. Нет единого пути для возвращения значений из Python 3.5
нативной сопрограмме или Python 2
генератора базирующегося на сопрограмме, так что все внутренние сопрограммы motor
, которые возвращают значения, были переписаны с помощью обратных вызовов.async and await
Награда за усилия потраченные на интеграцию с
Python 3.5
, состоит в том что теперь motor
работает с родной сопрограммой, написанной с учетом ключевых слов async
и await
синтаксис:async def f():
await collection.insert({'_id': 1})
Курсор из MotorCollection.find, MotorCollection.aggregate, или MotorGridFS.find может быть красиво и очень эффективно итегрирован в нативных сопрограммах (coroutines) с
async for
:async def f():
async for doc in collection.find():
print(doc)
Насколько эффективно? Для коллекции из 10 000 документов этот пример кода выполнялся за 0.14 секунды.
# Motor 0.5 with Tornado.
@gen.coroutine
def f():
cursor = collection.find()
while (yield cursor.fetch_next):
doc = cursor.next_object()
print(doc)
Следующий код, в котором просто заменены
gen.coroutine
и yield
на async
и await
и выполняет примерно тоже.# Motor 0.5 with Tornado, using async and await.
async def f():
cursor = collection.find()
while (await cursor.fetch_next):
doc = cursor.next_object()
print(doc)
Но с
async for
время работы занимает 0.04 секунды, то есть в три раза быстрее.# Motor 0.5 with Tornado, using async for.
async def f():
cursor = collection.find()
async for doc in cursor:
print(doc)
Однако, MotorCursor в to_list прежнему играет основную роль:
# Motor 0.5 with Tornado, using to_list.
async def f():
cursor = collection.find()
docs = await cursor.to_list(length=100)
while docs:
for doc in docs:
print(doc)
docs = await cursor.to_list(length=100)
Функция с
to_list
в два раза быстрее чем асинхронные, но это выглядит не так красиво и требует указания размера чанка. Я думаю, что async for
выглядит довольно стильно и работает достаточно быстро для того, чтобы его применять в большинстве случаев.Бета версии релизов
motor
публиковались далеко не всегда, но в этот раз по-другому. Интеграция asyncio
в motor
является совершенно новой. И поскольку это потребовало повсеместного рефакторинга ядра motor
, и переписывания существующей интеграции tornado
, была выпущена бета-версия для того, чтобы исправить все упущения.P.S. Просьба о грамматических ошибках и ошибках перевода писать в личку.
Комментарии (22)
ZZZ_Sochi
15.11.2015 18:29Мотор до сих пор не умеет третью монгу и «async for». Печаль…
Upd: прочитал пост. Когда я смотрел, «async for» не умел.Alex10
15.11.2015 18:42Уточню, не умеет с
PyMongo3
сMongodb3
вродеPyMongo2.8
работал.
СPyMongo3
подружитьMotor
обещают к весне.ZZZ_Sochi
15.11.2015 22:48+1Нет, pymongo2 с mognodb3 работет очень странно и не всегда предстказуемо. Например, любит тупо виснуть, блокируя приложение.
Так что надо апгрейдить мотор. Если никто не займётся, через месяцок займусь сам…Alex10
15.11.2015 22:58Ну ближайший месяц точно никто не займется, ближайший месяц разве что
motor0.5
стабильную версию выпустят.
lega
15.11.2015 18:50-1А как вы смотрите на то, что он пытается «замесить» asyncio с greenlet?
ZZZ_Sochi
15.11.2015 23:16+1Отрицательно. Не вижу ни малейшего практического смысла мешать ежа с ужом.
Если он есть, то буду рад про него узнать… :-)lega
16.11.2015 00:22И я так считаю, надо будет спросить автора Motor.
Наверно он использует гринлеты, что-бы не переписывать весь pymongo.ZZZ_Sochi
16.11.2015 13:01+1Я, если честно, не копал мотор так глубоко. Количество магии там за гранью моего понимания… Но в любом случае, это сейчас самая полная и стабильная библиотека для асинхронной работы с монгой. Пробовал оценить, сколько времени надо на то, чтобы написать свою и что-то у меня много выходило.
denis_g
15.11.2015 18:57Сам работаю с Python, но каждый раз, когда вижу различные асинхронные штуки на нём, в голове невольно возникает фраза «Тулить горбатого к стене».
Alex10
15.11.2015 19:07Вполне возможно, но многим все таки это помогает, мне например очень понравился
aiohttp
написанный поверхasyncio
, это очень удобно когда фреймворк умеет примерно все то же самое чтоflask
но с вебсокетами и причем использовать их с ним очень удобно.
Вот к примеру, сегодня буквально попалось, тем кто работает с blender тожеasyncio
пригодился.denis_g
15.11.2015 19:10+2Согласен. Очень радует, что язык растёт и развивается. Возможно, в версии 4 мы сможем увидеть и более красивый, «причесанный» вариант работы с асинхронностью.
novoxudonoser
15.11.2015 23:47Ну замечательно. Что всё таки дает asyncio против Tornado?
bosha
16.11.2015 00:17Где-то видел слайды с доклада, в которых разработчик уверял, что добился почти такой же производительности, как в tornado. Только без подключаемых библиотек на си. Но не берусь утверждать — сам не проверял.
stalkerg
17.11.2015 13:52+1Поддержка не от разработчиков Торнадо, а от сообщества Python. Ну и вроде фишек в нём по больше будет. Собственно Торнадо умеет работать с asyncio.
lega
17.11.2015 15:29+1Asyncio был создан как «стандарт», т.к. существующие асинхронные фреймворки (tornado, twisted, pulsar, cyclone.io...) как-бы «разрывают» питон на сегменты — сообщество тратят кучу ресурсов на дублирующие проекты, например — вместо того что-бы развивать один клиент MongoDB, разработчики пилят версию для tornado (asyncmongo) и отдельно версию для twisted (txmongo), можно было-бы вложится в один клиент для psql/websockets и др. но разработчики «вынуждены» пилить «клон» под свой фреймворк.
Теперь, с asyncio достаточно развивать только одну версию библиотек, т.к. все фреймворки которые совместимы с asyncio смогут (есть шанс) использовать весь набор асинхронных библиотек. В итоге это повышает эффективность разработки python проектов.
Ну и естественно в этот «стандарт» asyncio, было вложено куча работы, что-бы покрывать множество запросов разных фреймворков.
shrimpsizemoose
16.11.2015 02:57-1А на оригинал статьи, с которой приводится вольный перевод давать не принято? Или я не вижу просто?
BlessMaster
16.11.2015 03:17+1В топиках-переводах традиционно ссылка на оригинал и имя автора внизу статьи.
mx2000
сколько миллисекунд в секунде?)
Yahweh
точно, надо поправить на сантисекунды