У меня есть проект 10K LOC, написанный на Django с большим количеством Celery ( RabbitMQ ) для асинхронности и фоновых заданий, где это необходимо, и я пришел к выводу, что части системы выиграют от того, что будут переписаны в чем-то отличном от Django для лучшего параллелизма , Причины включают в себя:
- Обработка сигналов и изменяемые объекты. Особенно, когда один сигнал запускает другой, обработка их в Django с использованием ORM может вызывать удивление, когда экземпляры изменяются или исчезают. Я хочу использовать некоторый подход к обмену сообщениями, при котором передаваемые данные не изменяются в обработчике ( подход Clojure для копирования при записи кажется хорошим, если я правильно понял).
- Части системы не имеют веб-интерфейса и нуждаются в лучшей поддержке для одновременного выполнения задач. Например, система считывает метки NFC , и при считывании светодиод загорается на несколько секунд (задача Celery), воспроизводится звук (другая задача Celery) и запрашивается база данных (другая задача). Это реализовано как команда управления Django, но Django и его ORM, синхронные по своей природе и разделяющие память, ограничивают (мы думаем о добавлении большего количества считывателей NFC, и я не думаю, что подход Django + Celery больше будет работать, Я хотел бы видеть лучшие возможности передачи сообщений).
Каковы плюсы и минусы использования чего-то вроде Twisted или Tornado по сравнению с использованием такого языка, как Erlang или Clojure ? Я заинтересован в практической пользе и вреде.
Как вы пришли к выводу, что некоторые части системы будут лучше на другом языке? Вы испытываете проблемы с производительностью? Насколько серьезны эти проблемы? Если это может быть быстрее, важно ли это быстрее?
Пример 1: Django на работе вне HTTP-запроса:
- Тег NFC читается.
- База данных (и, возможно, LDAP) запрашивается, и мы хотим что-то сделать, когда данные станут доступны (красный или зеленый свет, воспроизводить звук). Это блокирует использование Django ORM, но пока есть работники Celery, это не имеет значения. Может быть проблема с большим количеством станций.
Пример 2: «передача сообщений» с использованием сигналов Django:
post_delete
Событие обрабатывается, другие объекты могут быть изменены или удалены из - за этого.- В конце уведомления должны быть отправлены пользователям. Здесь было бы неплохо, если бы аргументы, передаваемые обработчику уведомлений, были копиями удаленных или подлежащих удалению объектов и гарантированно не изменялись в обработчике. (Конечно, это можно сделать вручную, просто не передавая объекты, управляемые ORM, обработчикам.)
источник
Ответы:
Мысли открытия
Как вы пришли к выводу, что некоторые части системы будут лучше на другом языке? Вы испытываете проблемы с производительностью? Насколько серьезны эти проблемы? Если это может быть быстрее, важно ли это быстрее?
Однопоточная асинхронность
Есть несколько вопросов и других веб-ресурсов, которые уже имеют дело с различиями, плюсами и минусами однопоточной асинхронности и многопотокового параллелизма. Интересно прочитать о том, как однопоточная асинхронная модель Node.js работает, когда ввод-вывод является основным узким местом, и одновременно обрабатывается много запросов.
В Twisted, Tornado и других асинхронных моделях отлично используется однопоточная обработка. Поскольку во многих веб-программах много операций ввода-вывода (сеть, база данных и т. Д.), Время ожидания удаленных вызовов значительно увеличивается. Это время, которое можно потратить на другие вещи, такие как запуск других вызовов базы данных, рендеринг страниц и генерация данных. Использование этого единственного потока чрезвычайно высоко.
Одним из главных преимуществ однопоточной асинхронности является то, что она использует гораздо меньше памяти. В многопоточном исполнении каждому потоку требуется определенный объем зарезервированной памяти. По мере увеличения количества потоков увеличивается объем памяти, необходимый только для существования потоков. Поскольку память ограничена, это означает, что существуют ограничения на количество потоков, которые могут быть созданы в любой момент времени.
пример
В случае веб-сервера, притворяться, что каждый запрос имеет свой собственный поток. Допустим, для каждого потока требуется 1 МБ памяти, а на веб-сервере 2 ГБ ОЗУ. Этот веб-сервер будет способен обрабатывать (приблизительно) 2000 запросов в любой момент времени, прежде чем просто не хватит памяти для дальнейшей обработки.
Если ваша нагрузка значительно выше этой, запросы будут занимать очень много времени (при ожидании завершения более старых запросов), или вам придется добавить больше серверов в кластер, чтобы увеличить число одновременных запросов. ,
Многопоточный параллелизм
Многопоточный параллелизм вместо этого полагается на выполнение нескольких задач одновременно. Это означает, что если поток заблокирован в ожидании возврата из базы данных, одновременно могут быть обработаны другие запросы. Использование потоков ниже, но число выполняющихся потоков намного больше.
Многопоточный код также гораздо сложнее рассуждать. Есть проблемы с блокировкой, синхронизацией и другими забавными проблемами параллелизма. Однопоточная асинхронность не страдает от тех же проблем.
Однако многопоточный код гораздо эффективнее для задач, интенсивно использующих процессор . Если у потока нет никаких возможностей для «выдачи» - например, сетевого вызова, который обычно блокирует - однопотоковая модель просто не будет иметь никакого параллелизма вообще.
Оба могут сосуществовать
Конечно, между ними есть совпадение; они не являются взаимоисключающими. Например, многопоточный код может быть написан неблокирующим образом, чтобы лучше использовать каждый поток.
Суть
Есть много других вопросов для рассмотрения, но мне нравится думать о двух, как это:
В вашем конкретном случае вам необходимо определить, какая асинхронная работа выполняется, и как часто эти задачи возникают.
Там нет простого ответа. Вы должны рассмотреть, каковы ваши варианты использования, и спроектировать соответственно. Иногда лучше использовать асинхронную однопоточную модель. В других случаях требуется использование нескольких потоков для достижения массовой параллельной обработки.
Другие соображения
Есть и другие проблемы, которые вы должны рассмотреть, а не только модель параллелизма, которую вы выбираете. Вы знаете Erlang или Clojure? Как вы думаете, сможете ли вы написать безопасный многопоточный код на одном из этих языков, чтобы повысить производительность своего приложения? Потребуется ли много времени, чтобы освоить один из этих языков, и будет ли язык, который вы изучаете, принесет вам пользу в будущем?
Как насчет трудностей, связанных со связью между этими двумя системами? Будет ли слишком сложно поддерживать две отдельные системы параллельно? Как система Erlang будет получать задания от Django? Как Эрланг сообщит об этих результатах Джанго? Является ли производительность достаточно серьезной проблемой, что дополнительная сложность того стоит?
Последние мысли
Я всегда считал, что Django достаточно быстр, и он используется некоторыми сайтами с очень большой посещаемостью. Существует несколько способов оптимизации производительности, позволяющих увеличить количество одновременных запросов и время отклика. Следует признать, что до сих пор я ничего не делал с Celery, поэтому обычная оптимизация производительности, вероятно, не решит никаких проблем, которые могут возникнуть у вас с этими асинхронными задачами.
Конечно, всегда есть предложение использовать больше оборудования для решения этой проблемы. Является ли стоимость предоставления нового сервера дешевле стоимости разработки и обслуживания совершенно новой подсистемы?
Я задал слишком много вопросов на данный момент, но это было мое намерение. Ответ не будет легким без анализа и дальнейших подробностей. Возможность анализировать проблемы сводится к знанию задаваемых вопросов, хотя ... надеюсь, я помог в этом вопросе.
Мое инстинктивное чувство говорит, что переписывать на другом языке не нужно. Сложность и стоимость, вероятно, будут слишком велики.
редактировать
Ответ на продолжение
Ваше продолжение представляет несколько очень интересных случаев использования.
1. Django работает вне HTTP-запросов
Ваш первый пример включал чтение тегов NFC и запрос к базе данных. Я не думаю, что написание этой части на другом языке будет настолько полезным для вас, просто потому, что запрос к базе данных или серверу LDAP будет связан с сетевым вводом-выводом (и, возможно, производительностью базы данных). С другой стороны, количество одновременных запросов будет зависеть от самого сервера, поскольку каждая команда управления будет выполняться как собственный процесс. Будет время установки и демонтажа, которое влияет на производительность, так как вы не отправляете сообщения уже запущенному процессу. Однако вы сможете отправлять несколько запросов одновременно, поскольку каждый из них будет изолированным процессом.
В этом случае я вижу два пути, которые вы можете исследовать:
'OPTIONS': {'threaded':True}
.) На уровне базы данных или на уровне Django могут быть аналогичные параметры конфигурации, которые вы можете настроить для своей собственной базы данных. Независимо от того, на каком языке вы пишете запросы к базе данных, вам придется подождать, пока эти данные вернутся, прежде чем загорятся светодиоды. Однако производительность кода запроса может иметь значение, и Django ORM работает не слишком быстро ( но обычно достаточно быстро).Я не уверен, какой веб-сервер вы используете для Django.
mod_wsgi
для Apache позволяет вам настроить количество процессов и потоков внутри процессов, которые запрашивает служба. Обязательно настройте соответствующую конфигурацию вашего веб-сервера, чтобы оптимизировать количество обслуживаемых запросов.2. «Передача сообщений» с сигналами Джанго
Ваш второй вариант использования также довольно интересен; Я не уверен, что у меня есть ответы на это. Если вы удаляете экземпляры модели и хотите работать с ними позже, возможно, можно будет их сериализовать,
JSON.dumps
а затем десериализоватьJSON.loads
. Позже будет невозможно полностью воссоздать граф объектов (запрос связанных моделей), так как связанные поля лениво загружаются из базы данных, и эта ссылка больше не будет существовать.Другой вариант заключается в том, чтобы каким-либо образом пометить объект для удаления и удалить его только в конце цикла запрос / ответ (после обслуживания всех сигналов). Это может потребовать специального сигнала для реализации этого, а не полагаться на
post_delete
.источник
Я сделал очень сложную и масштабируемую разработку для крупного интернет-провайдера США . Мы сделали несколько серьезных транзакций с использованием сервера Twisted , и было сложно представить, чтобы Python / Twisted мог масштабировать все, что связано с процессором . Привязка ввода-вывода не является проблемой, но привязка к процессору была невозможна. Мы могли бы быстро собрать системы, но их масштабирование до миллионов одновременно работающих пользователей было бы кошмаром конфигурации и сложности, если бы они были связаны процессором.
Я написал в блоге об этом, Python / Twisted VS Erlang / OTP .
TLDR; Эрланг победил.
источник
Практические проблемы с Twisted (которые я люблю и использую уже около пяти лет):
Я проделал небольшую работу, используя Node.js с CoffeeScript, и если одновременная производительность является вашей заботой, то это может стоить скачка.
Рассматривали ли вы запускать несколько экземпляров Django с некоторым механизмом распределения клиентов между экземплярами?
источник
Я предложу следующее, прежде чем рассмотреть возможность перехода на другой язык.
select
), что хорошо для ввода-вывода.Я не стал бы использовать многопоточность в Python, если приложение имеет приоритет в производительности. Я бы выбрал вышеупомянутый вариант, который может решить многие проблемы, такие как повторное использование программного обеспечения, подключение к Django , производительность, простота разработки и т. Д.
источник