Должен ли я придерживаться или отказаться от Python для борьбы с параллелизмом?

31

У меня есть проект 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-запроса:

  1. Тег NFC читается.
  2. База данных (и, возможно, LDAP) запрашивается, и мы хотим что-то сделать, когда данные станут доступны (красный или зеленый свет, воспроизводить звук). Это блокирует использование Django ORM, но пока есть работники Celery, это не имеет значения. Может быть проблема с большим количеством станций.

Пример 2: «передача сообщений» с использованием сигналов Django:

  1. post_deleteСобытие обрабатывается, другие объекты могут быть изменены или удалены из - за этого.
  2. В конце уведомления должны быть отправлены пользователям. Здесь было бы неплохо, если бы аргументы, передаваемые обработчику уведомлений, были копиями удаленных или подлежащих удалению объектов и гарантированно не изменялись в обработчике. (Конечно, это можно сделать вручную, просто не передавая объекты, управляемые ORM, обработчикам.)
Саймон Панцаре
источник
Я думаю, что лучшие ответы будут, если вы объясните больше, почему вы пришли к выводу
Уинстон Эверт
5
Прежде чем кто-то скажет, что вопросы о выборе языка не по теме, я скажу, что думаю, что это хорошо, так как это практическая проблема с конкретными требованиями. Я надеюсь, что это делает некоторые подробные сравнения.
Адам Лир
Twisted является противоположностью одновременного! Это однопоточный сервер, управляемый событиями, он никуда вас не приведет, если вам нужен настоящий параллелизм.

Ответы:

35

Мысли открытия

Как вы пришли к выводу, что некоторые части системы будут лучше на другом языке? Вы испытываете проблемы с производительностью? Насколько серьезны эти проблемы? Если это может быть быстрее, важно ли это быстрее?

Однопоточная асинхронность

Есть несколько вопросов и других веб-ресурсов, которые уже имеют дело с различиями, плюсами и минусами однопоточной асинхронности и многопотокового параллелизма. Интересно прочитать о том, как однопоточная асинхронная модель Node.js работает, когда ввод-вывод является основным узким местом, и одновременно обрабатывается много запросов.

В Twisted, Tornado и других асинхронных моделях отлично используется однопоточная обработка. Поскольку во многих веб-программах много операций ввода-вывода (сеть, база данных и т. Д.), Время ожидания удаленных вызовов значительно увеличивается. Это время, которое можно потратить на другие вещи, такие как запуск других вызовов базы данных, рендеринг страниц и генерация данных. Использование этого единственного потока чрезвычайно высоко.

Одним из главных преимуществ однопоточной асинхронности является то, что она использует гораздо меньше памяти. В многопоточном исполнении каждому потоку требуется определенный объем зарезервированной памяти. По мере увеличения количества потоков увеличивается объем памяти, необходимый только для существования потоков. Поскольку память ограничена, это означает, что существуют ограничения на количество потоков, которые могут быть созданы в любой момент времени.


пример

В случае веб-сервера, притворяться, что каждый запрос имеет свой собственный поток. Допустим, для каждого потока требуется 1 МБ памяти, а на веб-сервере 2 ГБ ОЗУ. Этот веб-сервер будет способен обрабатывать (приблизительно) 2000 запросов в любой момент времени, прежде чем просто не хватит памяти для дальнейшей обработки.

Если ваша нагрузка значительно выше этой, запросы будут занимать очень много времени (при ожидании завершения более старых запросов), или вам придется добавить больше серверов в кластер, чтобы увеличить число одновременных запросов. ,


Многопоточный параллелизм

Многопоточный параллелизм вместо этого полагается на выполнение нескольких задач одновременно. Это означает, что если поток заблокирован в ожидании возврата из базы данных, одновременно могут быть обработаны другие запросы. Использование потоков ниже, но число выполняющихся потоков намного больше.

Многопоточный код также гораздо сложнее рассуждать. Есть проблемы с блокировкой, синхронизацией и другими забавными проблемами параллелизма. Однопоточная асинхронность не страдает от тех же проблем.

Однако многопоточный код гораздо эффективнее для задач, интенсивно использующих процессор . Если у потока нет никаких возможностей для «выдачи» - например, сетевого вызова, который обычно блокирует - однопотоковая модель просто не будет иметь никакого параллелизма вообще.

Оба могут сосуществовать

Конечно, между ними есть совпадение; они не являются взаимоисключающими. Например, многопоточный код может быть написан неблокирующим образом, чтобы лучше использовать каждый поток.


Суть

Есть много других вопросов для рассмотрения, но мне нравится думать о двух, как это:

  • Если ваша программа связана с вводом / выводом , то однопоточная асинхронность, вероятно, будет работать достаточно хорошо.
  • Если ваша программа связана с процессором , то лучше всего будет использовать многопоточную систему.

В вашем конкретном случае вам необходимо определить, какая асинхронная работа выполняется, и как часто эти задачи возникают.

  • Они встречаются при каждом запросе? Если это так, память, вероятно, станет проблемой по мере увеличения количества запросов.
  • Упорядочены ли эти задачи? Если это так, вам придется рассмотреть вопрос о синхронизации при использовании нескольких потоков.
  • Являются ли эти задачи интенсивным использованием процессора? Если да, может ли один поток справиться с нагрузкой?

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

Другие соображения

Есть и другие проблемы, которые вы должны рассмотреть, а не только модель параллелизма, которую вы выбираете. Вы знаете Erlang или Clojure? Как вы думаете, сможете ли вы написать безопасный многопоточный код на одном из этих языков, чтобы повысить производительность своего приложения? Потребуется ли много времени, чтобы освоить один из этих языков, и будет ли язык, который вы изучаете, принесет вам пользу в будущем?

Как насчет трудностей, связанных со связью между этими двумя системами? Будет ли слишком сложно поддерживать две отдельные системы параллельно? Как система Erlang будет получать задания от Django? Как Эрланг сообщит об этих результатах Джанго? Является ли производительность достаточно серьезной проблемой, что дополнительная сложность того стоит?


Последние мысли

Я всегда считал, что Django достаточно быстр, и он используется некоторыми сайтами с очень большой посещаемостью. Существует несколько способов оптимизации производительности, позволяющих увеличить количество одновременных запросов и время отклика. Следует признать, что до сих пор я ничего не делал с Celery, поэтому обычная оптимизация производительности, вероятно, не решит никаких проблем, которые могут возникнуть у вас с этими асинхронными задачами.

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

Я задал слишком много вопросов на данный момент, но это было мое намерение. Ответ не будет легким без анализа и дальнейших подробностей. Возможность анализировать проблемы сводится к знанию задаваемых вопросов, хотя ... надеюсь, я помог в этом вопросе.

Мое инстинктивное чувство говорит, что переписывать на другом языке не нужно. Сложность и стоимость, вероятно, будут слишком велики.


редактировать

Ответ на продолжение

Ваше продолжение представляет несколько очень интересных случаев использования.


1. Django работает вне HTTP-запросов

Ваш первый пример включал чтение тегов NFC и запрос к базе данных. Я не думаю, что написание этой части на другом языке будет настолько полезным для вас, просто потому, что запрос к базе данных или серверу LDAP будет связан с сетевым вводом-выводом (и, возможно, производительностью базы данных). С другой стороны, количество одновременных запросов будет зависеть от самого сервера, поскольку каждая команда управления будет выполняться как собственный процесс. Будет время установки и демонтажа, которое влияет на производительность, так как вы не отправляете сообщения уже запущенному процессу. Однако вы сможете отправлять несколько запросов одновременно, поскольку каждый из них будет изолированным процессом.

В этом случае я вижу два пути, которые вы можете исследовать:

  1. Убедитесь, что ваша база данных способна обрабатывать несколько запросов одновременно с пулами соединений. (Например, Oracle требует соответствующей настройки Django 'OPTIONS': {'threaded':True}.) На уровне базы данных или на уровне Django могут быть аналогичные параметры конфигурации, которые вы можете настроить для своей собственной базы данных. Независимо от того, на каком языке вы пишете запросы к базе данных, вам придется подождать, пока эти данные вернутся, прежде чем загорятся светодиоды. Однако производительность кода запроса может иметь значение, и Django ORM работает не слишком быстро ( но обычно достаточно быстро).
  2. Минимизируйте время установки / демонтажа. Иметь постоянно запущенный процесс и отправлять ему сообщения. (Поправьте меня, если я ошибаюсь, но именно на этом на самом деле сосредоточен ваш первоначальный вопрос.) Написан ли этот процесс на Python / Django или другом языке / фреймворке, как описано выше. Мне не нравится идея использовать команды управления так часто. Можно ли иметь постоянно работающий небольшой фрагмент кода, который отправляет сообщения от читателей NFC в очередь сообщений, которую Celery затем читает и пересылает в Django? Установка и демонтаж небольшой программы, даже если она написана на Python (но не Django!), Должна быть лучше, чем запуск и остановка программы Django (со всеми ее подсистемами).

Я не уверен, какой веб-сервер вы используете для Django. mod_wsgiдля Apache позволяет вам настроить количество процессов и потоков внутри процессов, которые запрашивает служба. Обязательно настройте соответствующую конфигурацию вашего веб-сервера, чтобы оптимизировать количество обслуживаемых запросов.


2. «Передача сообщений» с сигналами Джанго

Ваш второй вариант использования также довольно интересен; Я не уверен, что у меня есть ответы на это. Если вы удаляете экземпляры модели и хотите работать с ними позже, возможно, можно будет их сериализовать, JSON.dumpsа затем десериализовать JSON.loads. Позже будет невозможно полностью воссоздать граф объектов (запрос связанных моделей), так как связанные поля лениво загружаются из базы данных, и эта ссылка больше не будет существовать.

Другой вариант заключается в том, чтобы каким-либо образом пометить объект для удаления и удалить его только в конце цикла запрос / ответ (после обслуживания всех сигналов). Это может потребовать специального сигнала для реализации этого, а не полагаться на post_delete.

Джош Смитон
источник
1
много FUD и сомнений в блокировке и других вещах, которые не являются проблемами с Erlang, ни одна из традиционных проблем общего состояния, которые вы перечисляете, не связана с языком и средой выполнения, специально разработанными, чтобы не делить состояние. Erlang может обрабатывать десятки тысяч незаметных процессов за очень короткое время, нехватка памяти также не является проблемой.
@ Джаррод, я лично не знаю Эрланга, поэтому я приму то, что вы говорите по этому поводу. В остальном, почти все остальное, что я упомянул, уместно. Стоимость, сложность и то, используются ли текущие инструменты должным образом или нет.
Джош Смитон
Это такой эпический ответ, который я действительно люблю читать ^^. +1, хорошая работа!
Лоран Бурго-Рой
Также, если у вас есть шаблоны DJango, их можно использовать в эрланге с Erlydtl
Zachary K
8

Я сделал очень сложную и масштабируемую разработку для крупного интернет-провайдера США . Мы сделали несколько серьезных транзакций с использованием сервера Twisted , и было сложно представить, чтобы Python / Twisted мог масштабировать все, что связано с процессором . Привязка ввода-вывода не является проблемой, но привязка к процессору была невозможна. Мы могли бы быстро собрать системы, но их масштабирование до миллионов одновременно работающих пользователей было бы кошмаром конфигурации и сложности, если бы они были связаны процессором.

Я написал в блоге об этом, Python / Twisted VS Erlang / OTP .

TLDR; Эрланг победил.


источник
4

Практические проблемы с Twisted (которые я люблю и использую уже около пяти лет):

  1. Документация оставляет желать лучшего, и модель все равно довольно сложна для изучения. Мне трудно заставить других программистов на Python работать с Twisted кодом.
  2. В итоге я использовал блокировку файлового ввода-вывода и доступ к базе данных из-за отсутствия хороших API блокировки. Это действительно может повредить производительности.
  3. Кажется, не существует огромного сообщества и здорового сообщества, использующего Twisted; например Node.js имеет гораздо более активную разработку, особенно для веб-программирования.
  4. Это все еще Python, и по крайней мере CPython не самая быстрая вещь.

Я проделал небольшую работу, используя Node.js с CoffeeScript, и если одновременная производительность является вашей заботой, то это может стоить скачка.

Рассматривали ли вы запускать несколько экземпляров Django с некоторым механизмом распределения клиентов между экземплярами?

Дикон рид
источник
1
Документация Python в целом оставляет желать лучшего: / (Не говоря уж о том , что это так плохо, но для популярного языка можно ожидать, что он будет намного лучше).
Ладья
3
Я считаю, что документация по Python и, в частности, документация по Django являются одними из лучших документов для любого языка. Хотя многие сторонние библиотеки оставляют желать лучшего.
Джош Смитон
1

Я предложу следующее, прежде чем рассмотреть возможность перехода на другой язык.

  1. Используйте LTTng для записи системных событий, таких как сбои страниц, переключение контекста и ожидания системных вызовов.
  2. Конвертируйте, где бы ни потребовалось слишком много времени, чтобы использовать библиотеку C и использовать любой шаблон проектирования, который вам нравится (многопоточность, сигнальные события, асинхронный обратный вызов или традиционный Unix select), что хорошо для ввода-вывода.

Я не стал бы использовать многопоточность в Python, если приложение имеет приоритет в производительности. Я бы выбрал вышеупомянутый вариант, который может решить многие проблемы, такие как повторное использование программного обеспечения, подключение к Django , производительность, простота разработки и т. Д.

холмс
источник