Комментарии 36
Я сейчас скажу наверное ужасно глупую вещь, но вы знаете разницу между LIFO и FIFO? у вас уже есть redis, берёте Lists и вперёд. И будет вам выравнивание
Да, у нас Redis и для очереди мы как раз используем Redis list. Впрочем, к смыслу статьи это отношения не имеет, т. к. это могло быть что угодно: ApacheMQ, Rabbit, Kafka или самописный linked list — всё это подчиняется общим законам ))
Мысль с LIFO я вообще не понял. LIFO — это не очередь, а стек всё-таки. И я не понял, при чём тут LIFO.
Вопрос №1 — Что вы изменили в обработчике, чтобы он стал работать быстрее?
Вопрос №2 — Если у вас 3 потока обрабатывают сообщения и не справляются, то почему-бы не добавить ещё потоков? (Само-собой если не помогла оптимизация обработки)?
Другой вариант решения — подключать обработчики по мере наполения очереди. Т.е. приходит мало сообщений — работает 1 обработчик. Как только в очереди скопилось n-сообщений, включается ещё один поток и т.д. Как только очередь отчистилась, то потоки отключаются по-одному до достижения оптимальной загрузки. Недостаток этого решения — могут возникнуть блокировки на извлечении сообщений из очереди. Решение — очереди для каждого потока + балансировщик.
Отсюда приходим к более продвинутому решению: сообщения сначала попадают в очередь балансировщика, который контролирует количество запущенных потоков обработки и перенаправляет сообщения в их очереди. У каждого потока — своя очередь. При опустошении очереди и неполучении новых в течении определённого времени балансировщик отключает поток. При заполнении потоков существующих обработчиков балансировщик создаёт новый. Балансировщик в данной системе должен быть максимально легковесным, чтобы не добавлять задержки в обработку. В идеале он должен быть прозрачным для случая, когда задействована только одна очередь обработки.
Как в магазинах: если вы ждёте в очереди больше 5 минут, лучше звоните солу"
Читать https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
Вопрос №2: Даа, всё это можно, но там потоки начнут лочить друг у друга запись в БД и их увеличение не приводит к увеличению пропускной способности сервиса. В своё время решили остановиться на трёх, хотя эксперименты с увеличением обработчиков ещё можно было бы провести. Идея с «балансировщиком» хороша лишь до некоторой степени: вы про закон Амдала и его обощение — Universal Scalability Law слышали? Распараллеливание обработчиков нельзя делать бесконечно: начиная с какого-то момента оно становится бесполезным, а потом в какой-то момент и вредным, т. е. есть оптимальное число распараллеливания, за которое переступать нельзя.
По поводу комментария про объекты на запись — это очень важный момент. Пример из жизни: сервер запускал тяжёлое задание по планировщику и выполнял его несколько часов с интенсивной работой с базой данных. Когда время стало выходить за рамки разумного, то решили оптимизировать это с минимальной кровью. Так вот, замена жёстких дисков на SSD на сервере базы данных дала прирост производительности в 4 раза. Так что иногда вместо оптимизации программного обеспечения можно найти простой и дешёвый способ аппаратной оптимизации.
Очевидно LIFO это не очередь.
И еще — математики Вы много написали, но непонятно — в чем она Вам помогла? Разобраться с задержками при обработке сообщений можно было и без нее.
Расскажите, где я не прав?
Оказывается, что на заданном интервале времени количество обрабатываемых заданий должно быть существенно больше количества генерируемых. Тогда будет хорошо.
Если же, как Вы пишете, имеет место «одинаковое количество обработанных и сгенерированных, или несколько больше» — то будет иметь место близость к точке насыщения и система пойдёт вразнос, что мы и наблюдали. Это простое, но контринтуитивное следствие теории очередей. Вот чего я хотел бы знать раньше!
в реальных системах количество ядер ограничено и, поэтому, с некоторого момента, необходимо закидывать обработчики и генераторы по несколько штук в один поток (свой для генераторов и свой для обработчиков). Иначе ресурсы системы будут заняты не работой, а обслуживанием потоков. Грубо говоря, у нас 32 ядра. Делаем по 15 потоков генераторов и по 25 обработчиков, далее все генераторы равномерно распределяем по этим потокам, аналогично с обработчиками. Да, некоторые будут блокировать друг друга, но все равно, при 50 реально работающих потоков аналогично будет проходить блокировка, но уже на уровне ОС.
Режим, близкий к насыщению: μ>λ, но ненамного. В этой ситуации небольшие изменения пропускной способности сервера очень сильно влияют на параметры производительности системы, как в ту, так и в другую сторону.
Не об это-ли постоянно спотыкаются проекты, использующие гибкие методологии разработки типа Scrum и Kanban? Я имею ввиду то, что в них так же используются очереди задач: бэклог, спринты.
Если я правильно понял тезис, попытке задать нагрузку на команду в режиме близком к насыщению, проект автоматически попадает в зону риска, что задачи будут находиться в очереди вечность. И даже незначительные улучшения в «пропускной способности» разработчиков (например за счет овертаймов) будут оказывать сильное влияние на производительность команды и положительно сказываться на ситуации в целом.
Интересная статья спасибо! Математика) и действительно! Количество обратываемых сообщений должно быть намного больше в нормально настроенной системе. Никогда об этом не думал, а так оно и есть… Однако это ли Вам помогло найти ботлнек!?
На мой взгляд, мониторинг подсистем, хотя бы с визуализацей загружености. Например для БД: диск IO, количество блокировок БД, топ тяжёлых запросов и их медиана в зависимости от количества воркеров (одновременно выполняемых задач), показали бы картину без математики. Если вы наращиваете воркеров, а медиана времени выполнения тяжёлых запросов растет, то сколько бы их не добавлять и как не прокачивать app сервера — это не поможет, затык в БД. А значит надо, что-то соптимизировать, что и было сделано — кэширование ненужных запросов к БД.
Отсюда можно разбить задачу на две:
— Обрабатывать текущий постоянный поток (если он вообще есть).
— Обрабатывать пики.
И можно даже ввести некоторый критерий применимости теории очередей:
Если пиковая нагрузка не превышает среднюю на X процентов, можно не пытаться обрабатывать пики отдельно и просто добавить мощности в потоковый обработчик. Но, если пиковая нагрузка значительно выше средней, внезапные горы сообщений обрабатывать нужно отдельно и теория очередей в указанном изложении (не сомневаюсь, атм про пики тоже есть) не поможет.
Это все можно математически формализовать, я просто хотел показать, что случай может несколько отличаться, от того, как он представлен.
Что не делает статью плохой, наоборот, она, по-моему, прекрасна, и формализм, на самом деле, радует, ибо дает понятные и обоснованные метрики, а не «ну нам показалось».
Кажется, что задача просто изначально сформулирована несколько неполностью, отсюда и проблемы… когда внезапно приходит большая пачка сообщений, 90% этой пачки должны быть обработаны за 4 секунды. В этом случае средняя скорость обработки и средняя скорость поступления сообщений не играют никакой роли, не важно, что после пачки будет пусто, её вот прямо сейчас надо обрабатывать. Усреднение только мешает и сбивает с толку.
Хорошая мысль, но боюсь, она еще больше сбивает с толку: http://xkcd.ru/605/ :)
Да, я знаю, что D/D/1 не вылетает в бесконечность при $\lambda=\mu$, но в статье про это не написал: мне кажется, что детерминированный источник это скорее абстракция) И это точно не наш случай: я, к сожалению, не могу тут рассказать конкретику, но источник событий у нас — самый что ни на есть классический пуассоновский. Мы должны улавливать внешние по отношению к системе события, и на их частоту мы влиять никак не можем.
https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D0%B9
Вещи, которые мне надо было знать прежде, чем создавать систему с очередью