Gearman – фреймворк для распределения задач, введение



    В этой статье, мне бы хотелось рассмотреть один из необычных способов оптимизации приложения, а именно использование проекта Gearman для распределения задач. Gearman является фреймворком для построения таких систем. Примеров кода в статье нет, статья больше вводная, хоть и содержит в себе достаточно практической информации.

    В любом достаточно сложном с точки зрения функциональности и нагруженности проекте рано или поздно встаёт вопрос оптимизации и масштабируемости. Известно множество подходов к решению данного комплекса задач, начиная от банального увеличения вычислительной мощности всей системы или особо нагруженных её частей, и заканчивая комплексными решениями в виде специализированных программно-аппаратных комплексов. Объединяет данные решения не только цель — быстрее, выше, сильнее, но и подход к их решению — поиск и определение слабых мест. Зачастую bottleneck это ресурсоёмкая задача связанная с обработкой графической информации, шифрованием, архивированием, тяжёлыми запросами к базе, обрабатывающими и/или возвращающими большой объём информации.

    Gearman — проект с открытым исходным кодом, который был разработан парнями из Danga Interactive. Название является не чем иным, как анаграммой к слову manager, и именно роль менеджера и является кратким описанием функциональности данного приложения — управление, контроль и распределение различных задач. Первоначально gearman был реализован на Perl, но со временем был переписан на C с использованием библиотеки libevent, наличие которой является необходим для функционирования основной части — сервера задач. Установка для любой *nix системы не представляет собой больших трудностей, а в большинстве дистрибутивов linux – пакет gearman входит в стандартный репозиторий.

    Здесь стоит объяснить почему автор употребил слово «фреймворк» в заголовке данной статьи. Дело в том, что использование gearman хоть и является относительно простым, в тоже время эффективное решение поставленной задачи потребует достаточно серьёзной разработки — это не готовое решение.

    Приложение, использующая Gearman в своей работе, работает с 3 основными компонентами:
  • сервер задач — является центральной частью Gearman, именно сюда будут приходить задачи и результаты от клиентов и исполнителей, и именно отсюда будут высылаться задания и результаты, исполнителям и клиентам соответственно.
  • исполнитель — является основной частью, где необходима реализация какой-либо функциональности. Исполнитель принимает и пытается выполнить задание от сервера.
  • клиент — также имплементируется отдельно, создаёт и отсылает задачу на сервер и, в некоторых случаях (о них — позже), получает результат.

    Клиент и исполнитель используют Gearman API для вызова и реализации необходимых функций. Схема, которая иллюстрирует вышеописанную архитектуру, приведена на основном сайте проекта:

image

    Рассмотрим классический пример работы такой системы — пользователь закачал фотографию на сайт, и хочет показать её всем своим друзьям. Необходимо быстро подготовить различные размеры данной фотографии, для использования по всему сайту. Да, до определённого момента все работы по генерации графики можно проводить в основном коде системы, а потом придёт хабраэффект, и вся система рухнет под наплывом желающих. Теперь рассмотрим как эту задачу можно решить с использованием Gearman — при получении фотографии, мы создаём задачу для gearman сервера, в которую входит необходимая информация о фотографии (в принципе возможно включить в задачу даже саму фотографию, но, мне кажется, это будет накладно, лучше использовать адрес) и отправляем её на выполнение. Сервер задач выбирает (round robin) свободного исполнителя (как Вы наверное уже догадались, исполнителей может быть несколько) и отсылает задачу на выполнение. Исполнитель получает всю необходимую информацию, обрабатывает её как нам бы этого и хотелось — и отправляет ответ об успешно выполненной задаче и, если задача была синхронная, результат работы. Да, задачи бывают двух типов — синхронные, когда клиент ждёт выполнения отправленной задачи, и соответственно результат, и асинхронные, когда клиент выступает только в роли инициатора задачи. Теперь добавляем сюда факт, что протокол общения между частями gearman приложения — TCP/IP, то есть подразумевается, что каждая часть (сервер, клиент, исполнитель) может находится на отдельной машине. Серверов и клиентов может быть несколько. Реализация клиента и исполнителя не зависят друг от друга (доступные языки в которых имплементирован API для работы с gearman включают в себя perl, php, python, java, C, MySQL UDF и др.). Проект активно развивается — новые версии с исправлением ошибок и улучшениями выходят каждый месяц. На выходе получаем довольно радужную картину, где спектр решаемых задач ограничен только фантазией и опытом разработчика. Для примера, несколько общих задач, которые успешно решались и решаются с использованием gearman:
  • вышеописанная задача с обработкой фотографии, причём не стоит ограничиваться только генерацией различных размеров
  • асинхронная инвалидация/обновление данных в кэше, без использования крона и задержек со стороны клиента
  • архивирование больших объёмов данных
  • работа с данными в формате — (ключ => значение), с использованием шифрования на стороне исполнителя

    Теперь немного практической информации. Основным объектом работы в gearman является задача — task, которая состоит из типа (синхронный, асинхронный), названия задачи (к примеру resize), и параметров — workload. Каждая задача идентифицируется по паре название/параметры. То есть если на сервер поступит две одинаковые задачи с одинаковыми параметрами, выполнена будет только первая, вторую сервер откинет. Из-за этого нюанса происходит много интересных вещей, поэтому всегда стоит помнить — необходимо стопроцентное выполнение конкретной задачи? — добавляй к параметрам уникальное значение.

    Сервер является процессом-демоном. Имеет достаточно много конфигурационных параметров, на ряду со стандартными для таких приложений настройками user-port-interface, имеется также несколько специфичных опций. Количество попыток выполнения задачи настраивается параметром -j, --job-retries=. Для использования нескольких потоков существует параметр -t, --threads=. Сервер имеет возможность сохранять очередь задач в какое-либо хранилище, чтобы при рестарте была возможность восстановить весь процесс без потери данных. Хранилищем может выступать MySQL/drizzle, memcached, PostgreSQL или sqllite. Более чем интересной является опция использования сторонних протоколов в процессе коммуникации клиент -> сервер задач. Единственным таким, имплементированным на данный момент, протоколом является HTTP. При помощи этой опции, можно настроить gearman сервер на приём задач по определённому порту через HTTP запрос (requested uri транслируется в task name, http body в workload, http headers соответственно в task type), то есть реализация клиента сводится к реализации простого HTTP клиента на вызывающей стороне. Никаких ограничений на тип/размер задачи это не накладывает. Подробнее об использовании HTTP в gearman можно почитать здесь. Используя HTTP протокол и Gearman построение эффективно сбалансированных REST сервисов может стать как никогда простой задачей. Cоздавать задачи для gearman можно практически из любого языка программирования, так как обёртки на вызовы функций API написаны для большинства сред.

    Исполняющая часть cоответственно находится в так называемом исполнителе. Здесь выбор средств реализации также очень обширен, начиная от стандартных php, python, java и заканчивая пользовательскими функциями в mysql. Единственное условие — наличие поддержки gearman библиотеки, что может потребовать дополнительной компиляции каких-либо модулей. Так называемый исполнитель (worker) является background процессом, после запуска регистрирует все задачи, которые он может выполнить и, находясь в памяти, ждёт поступления задач от сервера. Количество серверов, с которыми держит связь конкретный исполнитель — не ограничено.

    Поговорим о недостатках, их несколько, и они разной степени серьёзности. Так как исполнитель является долгоживущим процессом, то любое изменение его функциональности подразумевает под собой процесс перезапуска самого исполнителя, это же касается и добавления/удаления задач, которые может выполнить данный исполнитель. То есть, если нужно добавить новую функцию, или изменить уже существующую, нужно перезапустить все процессы исполнителей, которые связанны с выполнением данной задачей. Этот процесс, в случае, когда исполнители разбросаны по разным серверам, в разном количестве, не является тривиальным. Да и не все языки предназначены для написания эффективных процессов-демонов.

    Распределение задач сервером происходит только по одному единственному алгоритму — round robin. Да, существуют задачи с двумя уровнями приоритета — высокий и низкий. Но более точный контроль за выполнением задач недоступен. Более того, нельзя точно узнать, на каком из исполнителей будет выполнена задача, и также невозможно указать напрямую, которому узлу её послать, что сильно усложняет процесс отладки.

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

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

    Теперь немножко линков на источники информации и других интересностей о Gearman:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 40
  • 0
    Назначение Gearman и Capistrano пересекается хотя бы отчасти?
    • 0
      К сожалению не знаком с Capistrano чтобы точно сказать, но на первый взгляд — Capistrano более готовое решение, нацеленное на запуск скриптов на множестве серверов — такое легко реализуется через Gearman. Всё же Gearman продукт гораздо более широкого профиля.
      • 0
        Во главу угла в Gearman поставлена скорость и мастшабируемость.
      • 0
        По-моему Capistrano и Gearman могут хорошо использоваться в связке. Capistrano будет централизовано заливать новые версии исполнителей, убивать старые и запускать новые, то есть отчасти нивелирует основной недостаток Gearman.
      • 0
        Подробнее об использовании HTTP в gearman можно почитать здесь.


        Где?

        А вообще интерсная статья, спасибо, надо покопаться поглубже в продукте. Вопрос сразу возник — контроль за запуском/убийством исполнителей от самого Gearman не зависит? То есть реализовывать запуск исполнителя и его убийство после 1000 запросов во избежание ликов придётся внешними средствами? И ещё один — для асинхронных задач предусмотрена только потеря ответа — передача его куда-нибудь (параметром к другой задаче) не предусмотрена?
        • 0
          Ступил что-то насчёт первого вопроса, на разных же серверах процессы крутятся. Можно, конечно, и удалённый запуск реализовать, но не unix way.
          • 0
            Линк добавил, спасибо.

            Да, связи между исполнителями и сервером, кроме как приём-отправка задач — нет, тоесть контроль жизни worker'а полностью в Ваших руках. Естественно никто не мешает реализовать это в виде отдельной задачи :).
            И опять да, асинхронные задачи не подразумевают под собой дальнейшей работы с резултатом, но опять таки, можно вызвать из самого испольнителя ещё одну задачу с результатом, либо передать этот результат куда-нибудь ещё.
            • 0
              То есть в принципе ничто не мешает одному процессу выполнять роль и исполнителя (основную), и клиента (вспомогательную — ставить задачи для других исполнителей, а может и для себя).
              • 0
                Абсолютно верно, в этом плане — полная свобода.
            • –1
              Поддержку нужного количества исполнителей удобно реализовывать через supedvisord. Управляет он только локальными процессами, но там есть некоторые средства расширения, возможно получится сделать все что вы хотите.
              Выйти исполнитель может и сам, счетчик в цикле приема задач поставить дело недолгое.
            • +1
              Примерно для такой же задачи я реализовывал механизм распределения задач по алгоритмуround-robin
              Но мне необходим был полный контроль за процессом выполнения задания.
              Нужен был пользовательский интерфейс для настройки так называемых цепочек выполнения.

              Тогда мне почему не попался на глаза этот фреймворк.
              Надо будет поковырять его.
              Спасибо за наводку:)
              • 0
                Когда встал выбор сервера задач/очередей выбрал для себя Starling, т.к. он легковесный, поддерживает Memcache протокол, и клиенты есть во всех языках. Можно воркера хоть на чём писать. И ещё он очень быстрый. Но он конечно, не так наворочен, как Gearman. Нет приоритета задач, например.
                • 0
                  Gearman это не только сервер очередей, но и средство распределения работы. С помощью него достаточно удобно распараллеливать задачи аля Map/Reduce.
                  • 0
                    Да, согласен. Я и написал, что он не так наворочен как Gearman. Кстати, есть ещё Hadoop (это что касается Map-Reduce). Вообще, мне кажется надо выбирать исходя из ситуации и требований.
                    • 0
                      Согласен, и протокол memcached это удобно. Кстати насчет легковесности и скорости у gearman тоже все хорошо, сишный сервер кушает отсилы несколько мегабайт вместе с памятью на задачи и обрабатывает миллионы задач в день (например в Yahoo 6 миллионов задач в день проходит через gearman, в ЖЖ не знаю точно думаю порядок тот же).
                • –1
                  Отдельное спасибо за подробное описание минусов.
                  Кучу времени сэкономлено.
                  • +3
                    Хочется акцентировать внимание ещё на нескольких моментах:
                    1. gearman ещё и неплохое средство failover'a: ничто не мешает клинтской части ходить на несколько gearman серверов, ничто не мешает каждому воркеру зарегистрироваться на нескольких серверах. Ничто не мешает одни и те же воркеры запускать сразу на нескольких машинах. Ничто не мешает воркеру «браться за работу» только если есть ресурсы (воркер должен «забрать задачу», сервер только посылает уведомления о наличии задачи). И всё это вместе чудесно работает. Таким образом «с полпинка» реализуется отказоустойчивость всех используемых компонентов с довольно гибким распределением нагрузки — отказ или повышенная загрузка любого одного узла никак не влияет на работу системы в целом.
                    2. Данные, которые передаются воркеру (и обратно) — это по сути дела одна огромная JSON-encoded строка (успешно передавал ~100MB данных, прекрасно пропускает). Что в свою очередь накладывает некий overhead.
                    3. Для синхронных задач — это далеко не самое лучшее средство. Всё происходит всё-таки довольно неспешно. Задачи, где критично время отклика я бы рекомендавал распределять как-то иначе. Всю свою прелесть оно проявляет на задачах где надо «отдать кому-нибудь и пусть оно где-то отработает, где есть кому это сделать, а мы пока дальше пойдём».
                    4. Великолепная на мой взгляд фича (хоть и не для всех задач) на синхронных задачах (частично упоминалось выше): если два клиента пришлют одинаковый запрос — он будет выполнен только один раз, НО результат получат оба, что при определённых условиях может сэкономить море ресурсов.
                    • 0
                      Ничто не мешает воркеру «браться за работу» только если есть ресурсы (воркер должен «забрать задачу», сервер только посылает уведомления о наличии задачи).

                      Важный нюанс — завтра точно пойду копаться в рабочее время!
                      • 0
                        Блин, написал слово «нюанс» и засомневался — правильно ли написал, непривычно выглядит… Надо же так достать своими «ньюансами»… И ладно бы «в интернетах», а то на хабре…

                        P.S. Проверил по гуглу «нюанс» вс «нюанс» (3810 вс 165) — или гугл правду не говорит, или я превратился в граммар-наци
                        • 0
                          >> «нюанс» вс «нюанс»
                          В чем подвох?
                          • 0
                            Недостаток слепого метода, руки пишут правильно «нюанс», хотя имелось в виду " нюанс" вс «ньюанс»
                          • +1
                            Если проверять правописание по количеству найденных в выдаче гугла, то правильно писать «креведко»
                            • 0
                              Я проверял не правописание, а соотношение правильно/не правильно
                      • 0
                        Не понял, что произойдет если воркер взял задачу в обработку и упал на полпути?
                        Что будет с «плохой» задачей? Будет ли она отмечена как обработанная?
                        Если нет, то вернется ли она в очередь?
                        Если вернется, то как скоро и с каким приоритетом? На прежнее место или в конец? Не тормознет ли «плохая» задача всю очередь?

                        Как работают приоритеты? Сначала будут выполнены все задачи с высоким приоритетом, затем с низким, или есть какое-то чередование (2 с высоким, 1 с низким)?

                        Есть ли возможность перезапустить задания за определенный интервал времени?
                        • 0
                          Возможность потери задачи сведена к минимуму, она может покинуть сервер только в случае успешного выполнения, тоесть если воркер с задачей упал, воркер выкидывается (по таймауту) из пула, а задача возвращается в начало очереди. Приоритет не поменяется.

                          Общие приоритеты, на сколько я знаю, работают топорно — сначала высокий приоритет — потом низкий.

                          Вот возможность таймаута на задачу меня в своё время тоже интересовала, так как в документации ничего про это сказанно не было — полезли в код. И судя по всему они хотели это сделать, но так и не реализовали на данный момент :).
                          • 0
                            задача возвращается в начало очереди

                            Вот это и смущает. Получается, что несколько проблемных задач в начале очереди застопорят последующие задачи.
                            • 0
                              Только для того же воркера. Остальные будут работать как обычно.
                              У сервера есть событие, которое отсылается клиенту в случае ошибки на стороне воркера. У задания есть уникальный идентификатор. Можно посчитать количество перезапусков.
                              Вот только я не знаю, как убрать задание из очереди. Это надо документацию перечитывать.
                              • 0
                                Только для того же воркера. Остальные будут работать как обычно.

                                Я про ситуацию, когда проблемных заданий больше чем воркеров.
                                Не может получиться так, что воркер после перезапуска снова возмет проблемную задачу, которая вернулась в начало очереди?
                                • 0
                                  Так и будет. Воркер (необязательно именно тот же) будет брать задачу до тех пор, пока не будет достигнуто определенное число попыток.
                                  Полистал доку. У севера есть параметр -j (--job-retries), который регулирует количество повторных попыток. Работает глобально, что не всегда удобно.
                                  • 0
                                    И еще: важно знать, что значение по умолчанию для -j — не ограничивать вообще.
                                  • 0
                                    На сколько я помню, можно было задаче указать статус WORK_FAIL — и она просто будет отмечена как выполненая, но неуспешно. Только поддерживалось это не во всех клиентах на момент моих изысканий :). У них там в документации подробно расписан протокол и там есть много интересных вещей, которые я так понимаю ещё планируются к реализации, по примеру выше обсуждаемого job-timeout — CAN_DO_TIMEOUT.
                            • 0
                              На самом деле, я бы не стал сильно расчитывать на интеллектуальность распределения задач, по логике разработчиков, такие вещи лучше контролировать извне, ибо тут действительно — всем угодить очень сложно.
                            • 0
                              открытый вариант платного DataSynapse получается пишут
                              • 0
                                Для отладки чуть-чуть помогает mod_gearman под апач (если, конечно, он у вас есть).
                                В принципе управляющий протокол там не сложный, текстовый. Можно накидать простенький монитор используя всего две команды (status, workers), чтобы следить за тем, кто и что выполняет.
                                • 0
                                  Да, теже команды доступны через telnet на gearman сервер.
                                  • 0
                                    А из-под PHP можно понять, что сейчас выполняется? Сколько не смотрел доки — не нашел.
                                    • 0
                                      Нет, только через telnet/mod_gearman. Можно конечно написать мониторинг на php, это не будет сложным.
                                • 0
                                  А клиент при асинхронной задаче как-то оповещается о выполнении? Есть ли какой-то колбек?
                                  • 0
                                    Чем хуже/лучше Rabbit/Activq MQ?
                                    • 0
                                      Я тоже хотел бы знать ответ на последний вопрос: Чем хуже/лучше Rabbit/Activq MQ?

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