Pull to refresh

Comments 34

Зачем Вы указываете кодировку utf8 в заголовке файла? Проект же под третий питон? Не смог прочесть ни одного обзаца целиком: ощущение, что читаю слегка очеловеченный машинный перевод. То, что успел понять, не раскрыло для меня премуществ асинхронности против синхронности.
Зачем Вы указываете кодировку utf8 в заголовке файла? Проект же под третий питон?

Да, проект написан под 3ку. Относительно наличия «utf8» в заголовке каждого файла могу лишь сказать что это банальная привычка. На работе (наверное, как и многие) все также пишу на 2ой ветке.
под linux иногда могут быть проблемы (в 3.4) с русскими комментариями без utf8 в заголовке файла

чушь. Сказки для юных программистов.

Нельзя вызывать блокирующий синхронный код ( `.save()` и т.д.) из асинхронного.

Точнее, это можно делать пока у вас количество пользователей не больше десятка-двух.

Потом всё начнёт залипать.
В целом, согласен с вышесказанными. Этот момент реально стоит мне как-то продумать более детально, поскольку мы упираемся в количество имеющихся воркеров.
Были идеи сделать что-то в связке с aiopg, чтобы появилась возможность работы асинхронно с БД (хоть только и PostgreSQL). Правда возникает вполне простой вопрос: а как мне можно перейти в асинхронный код при работе aiorest-ws с Django, у которого множества различных адаптеров есть под разные базы?
Текущее решение не идеально, на текущий момент, но хоть есть от чего отталкиваться :)

Нельзя быть немного беременной, а потом надеяться как-то продумать ситуацию более детально.


Код или синхронный или — а\синхронный. Совмещать не удасться.

Справедливости ради, если всё остальное в норме, то можно сказать, что работа с БД просто ещё не реализована правильно… и этот частный, хоть и частый случай, по-моему можно таки продумать более детально чуть позже (естественно, до того как предлагать применять в продакшене).

Вероятно, у вас знакомство с asyncio исключительно теоретическое.
Чтобы переделать на корутины нужно менять сигнатуры кучи методов как минимум.
Получается новая библиотека, несовместимая со старой.

Вы абсолютно правы, я знаком с asyncio, да и с остальными async инструментами, сугубо теоретически, причём именно из-за озвученной вами проблемы — нужно всё переписать чтобы сделать что-то маломальски пользователе-ориентированное. Раз вы говорите, что всё так плохо, то, видимо, автору нужно приоритезировать решение этой проблемы.

Как-то на досуге портировал peewee под asyncio. В общем то работает, и интерфейс полностью сохраняется. Но вы правы, по сути вышла совсем новая библиотека. Но доводить решение до конца не стал по причине отсутствия понимания того, где это может пригодиться. Мне кажется из асинхронного приложения гораздо проще сделать вызов синхронного API, вместо сложных попыток приспособить не приспособленное.

Просто так вызывать блокирующий синхронный код нельзя — event loop залипает.
Запускать в thread pool — ненамного лучше. Под нагрузкой производительность начинает заметно проседать.


Остается два выхода:


  1. Писать свои правильные асинхронные библиотеки
  2. Поднять руку, резко её опустить и сказать: "И зачем мне эта асинхра? Буду до пенсии писать на Django!".

Первый вариант интересный и познавательный, второй — спокойный и стабильный.

Я имел в виду проще вызывать из асинхронного кода синхронные API методы через асинхронный HTTP клиент (который в большинстве асинхронных фреймворков идет из коробки). В этом случае асинхронный компонент выполняет только те задачи, которые требуют асинхронного подхода, а все остальное делегируется простым в реализации синхронным решениям вроде того же Django.

В случае же с БД обойтись без Django вообще почти не вариант. Там и админка, и генерация/применение/откат миграций, и удобный отлаженный ORM. На перенос всего этого хозяйства в асинхронный компонент уйдут годы. В промежуточный же этап в большинстве случаев придется поддерживать дублирование кода (например, описывать модели нужно будет в обоих компонентах).
  1. Не думаю что это рабочее решение. С таким же успехом можно мастерить асинхронный Python код который для доступа к базе дергает Go или JavaScript. Меня от этой идеи в дрожь бросает (и не потому что я Go/JavaScript недолюбливаю).


  2. Django — не ко всем бочкам затычка. И эта затычка очень часто оказывается не той формы как дырка. Как ORM она ужасна.
Решений на свете много разных, и все как правило хороши в какой-то определенной области применения. Go/Javascript хороши тем, что сами могут выступать в роли легковесной асинхронной обертки над тяжелыми «все-в-одном» синхронными фреймворками. Кстати, весьма интригующе в этом ключе звучит ответ Игоря Сысоева на вопрос о том, чем он сейчас занимается.

А Django-затычка как раз чаще именно той формы, что и дырка — это мое мнение :) В конце концов всегда можно расширить/допилить недостающий функционал, архитектура Django это позволяет.

Насчет ORM, по большому счету все они ужасны. Я думаю, что идея ORM не в том, чтобы предоставлять более удобный способ выполнять SQL запросы, а в том, чтобы объединить под одной общей абстракцией такие вещи как: описание модели, генерация/применение/откат миграций и осуществление самих запросов. Сложно представить решение, которое все перечисленное реализует при помощи нативного SQL — поддержание консистентного состояния системы в этом случае будет задачей очень сложной.
В тему вебсокетов, столкнулся с реализацией клиента на aiohttp, который должен и слушать сокет и отправлять сообщения. Пришлось помучиться.

В итоге остановился на такой реализации, в доке к websockets описана, псевдокод:
async def receive():
    """ Слушаем сокет """

async def message_to_sent():
    """ Ждем сообщение для отправки """

While True:
    listener_task = asyncio.ensure_future(receive())
    sender_task = asyncio.ensure_future(message_to_sent())

    done, pending = await asyncio.wait(
        [listener_task, sender_task], return_when=asyncio.FIRST_COMPLETED)

    if listener_task in done:
        # Процессинг входящих сообщений
        handler(listener_task.result())
    else:
        listener_task.cancel()

    if sender_task in done:
        # Отправка
        send_str(sender_task.result())
    else:
        sender_task.cancel()


Или это можно сделать более «красиво»?
`ws.send_str()` не корутина если что.
Это намек
Это я знаю, а намёк не понял ) Будет «залипать» при большом количестве исходящих сообщений?

А какие еще варианты для websocket клиента?

sender_task не нужен, шлите данные откуда есть

4) Здорово, теперь кое что прояснилось.


И после этого читать не получается. )
Потому что не прояснилось и потому что тяжело и лениво вникать.

Для кого и для чего материал?
Если это тутор, то на какой уровень подготовки?

Если это описание фреймворка, то слишком абстрактный пример и много «воды».
Если это подход к построению фреймворка, то слишком много ненужных в этом случае деталей…

Браво! Я просто не могу подобрать слов! Тема WebSockets в Python для меня была просто пыткой, сколько я ни пытался заставить себя погрузиться в неё, всё время какое-то отторжение происходило и я быстро находил на что отвлечься. aiorest-ws (по крайней мере по примерам и описанию) — это огромный шаг в сторону упрощения.


Ваша реализация в стиле Flask мне импонирует, как и использование REST подхода. Swagger (OpenAPI) вам не факт, что поможет в плане какой-то готовой реализации (по крайней мере я не слышал о REST WebSockets поддержке ни в Swagger-UI, ни в Swagger-Codegen), но для HTTP RESTful API он просто божесвеннен, на мой взгляд, и я даже собрал демо на Flask-RESTplus (фреймворк для HTTP REST Swagger API) для более-менее жизненного примера, может что-то интересное для себя и в нём найдёте.


Кстати, а ваш роутинг можно на модули разбивать, например, как Blueprint в Flask? Это гораздо удобнее, на мой взгляд, чем один общий router где-то там в корне проекта.


Я вижу, что ваши сериализаторы очень похожи на Marshmallow, но почему бы просто не взять сам Marshmallow вместо нового велосипеда? Неужели требование к минимальности зависимостей настолько строгое?

Браво! Я просто не могу подобрать слов! Тема WebSockets в Python для меня была просто пыткой, сколько я ни пытался заставить себя погрузиться в неё, всё время какое-то отторжение происходило и я быстро находил на что отвлечься. aiorest-ws (по крайней мере по примерам и описанию) — это огромный шаг в сторону упрощения.

Я бы сказал немного попроще – эксперимент. На текущий момент времени чего-то готового и работающего из коробки с веб-сокетами, в привычном стиле я так и не нашел (да и сейчас, вроде как ситуация не особо поменялась). Да, есть что-то вроде расширений (или плагинов) для того же Django REST, но немного не то, чего я ожидал бы увидеть.
Вполне возможно, что существуют какие-то «закрытые» реализации подобных библиотек, которые имеют что-то схожее с тем, что я постарался описать, но не является open source. А жаль.

Ваша реализация в стиле Flask мне импонирует, как и использование REST подхода. Swagger (OpenAPI) вам не факт, что поможет в плане какой-то готовой реализации (по крайней мере я не слышал о REST WebSockets поддержке ни в Swagger-UI, ни в Swagger-Codegen), но для HTTP RESTful API он просто божесвеннен, на мой взгляд, и я даже собрал демо на Flask-RESTplus (фреймворк для HTTP REST Swagger API) для более-менее жизненного примера, может что-то интересное для себя и в нём найдёте.

Какого-то конкретного решения о структуре проекта (вроде тех, что предлагаются в Django или Flask) я не описывал. Пока просто выбирайте тот стиль что удобнее будет вам.
Спасибо за ссылку, посмотрю на досуге. Вдруг чего-нибудь возьму на вооружение.

Кстати, а ваш роутинг можно на модули разбивать, например, как Blueprint в Flask? Это гораздо удобнее, на мой взгляд, чем один общий router где-то там в корне проекта.

В реализации класса SimpleRouter есть метод include, который принимает другой роутер в качестве параметра. Внутри нее просто копируются роуты из одного в другой. То есть конечный роутер будет иметь пути нескольких роутеров: свой, и те, что мы включили через вызов include.

Я вижу, что ваши сериализаторы очень похожи на Marshmallow, но почему бы просто не взять сам Marshmallow вместо нового велосипеда? Неужели требование к минимальности зависимостей настолько строгое?

Лишние трудности закаляют (надо же было себе какой-то челенж придумать), да и сделать что-то свое всегда интересно. Исходники библиотеки Marshmallow во многом хорошо подсказывали как решать ту или иную задачу при сериализации классов SQLAlchemy.
Хотя такая библиотека и есть, но нет необходимости её использовать, поскольку нам требуется лишь несколько полей. Быстрее (и проще) будет сделать что-то минималистичное самостоятельно.
При обсуждении непосредственно и возникла идея, что было бы классно иметь достаточно «гибкий» фреймворк, который использует веб-сокеты, через которые данные циркулируют в обе стороны.

Все же почему вебсокеты?

Все просто. Если у тебя стоит задача получать данные в реальном времени, то при использовании HTTP чтобы получать свежие данные вы будете использовать опрос сервера с неким интервалом времени, что не очень удобно. В случае с веб-сокетами достаточно создать соединение с сервером один раз, подписаться на получение каких-то данных, и получать свежие данные от сервера без необходимости выполнять какой-либо long polling.

В описанном вами кейсе я бы выбрал SSE (Server-Sent Events). И все свелось у меня к тому что запросы к серверу шли бы обычным образом без веб сокетов и прочих (тут много плюсов), а события по SSE каналу.

Автор, тебе надо просто огромными буквами написать в github репозитарии, что твой фреймворк ни разу не асинхронный. Каждое обращение к БД в твоём фреймворке — это блокировка потока, а обращаться к БД получается надо будет почти всегда, и соответственно ты будешь блокировать event loop на каждый request.

Есть aiopg который позволяет использовать query-builder алхимии, и никто не пытается примостить ORM алхимии до кучи к нему. Во первых — это не особо кому-то и нужно, во-вторых — это совсем не тривиальная задача.

В общем случае, я бы на твоём месте прикрутил поддержку aiopg, переписал все твои вьюхи чтобы они сериализовали/десериалзовали объекты. Всю работу с БД (чтение, запись и т.д.) вынес бы в отдельные методы (корутины) которые надо было бы реализовывать пользователю библиотеки.
Выбор был основан в некоторой мере и на личных предпочтениях. С Tornado мне приходилось иметь дело, но предоставляемый API «из коробки» не очень нравится.
tornado с выходом asyncio несколько потерял актуальность.
Sign up to leave a comment.

Articles