Очереди и блокировки. Теория и практика

в 15:08, , рубрики: highload junior, RabbitMQ, александр календарёв, Анализ и проектирование систем, Блог компании Конференции Олега Бунина (Онтико), высокая производительность, Программирование, Проектирование и рефакторинг, метки:

Мы выдохнули после HighLoad++ и продолжаем публикации лучших докладов прошлых лет. HighLoad++ получился прекрасным, количество организационных улучшений скачкообразно переросло в новое качество продукта. Хабр, кстати, вёл текстовую трансляцию с конференции (первый, второй дни).

Александр Календарёв

Александр Календарев ( akalend )

Здравствуйте, уважаемые коллеги! Мой доклад будет про вещь, без которой не обходится ни один HighLoad-проект — про сервера очередей, и если успею, то расскажу про блокировки (примечание расшифровщика — успел :).

Очереди и блокировки. Теория и практика - 2

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

Очереди и блокировки. Теория и практика - 3

Т.к. наша конференция называет HighLoad Junior, я хотел бы пойти от Junior-проекта. Есть у нас типичный Junior-проект — это какая-то веб-страница, которая обращается к базе. Может быть, это электронный магазин или еще что-то там. И вот, к нам пошли-пошли пользователи, и на каком-то этапе мы получили ошибочку (может быть и другая ошибка):

Очереди и блокировки. Теория и практика - 4

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

Очереди и блокировки. Теория и практика - 5

Пошли еще пользователи, и еще пользователи, и у нас появилась еще одна ошибочка:

Очереди и блокировки. Теория и практика - 6

Тогда мы полезли в логи, посмотрели, как можно отмасштабировать SQL-сервер. Нашли и сделали репликацию.

Очереди и блокировки. Теория и практика - 7

Но тут у нас в MySQL полезли ошибки:

Очереди и блокировки. Теория и практика - 8

Ну, ошибки могут быть и в более простых конфигурациях, это я тут образно показал.

И в этот момент мы начинаем задумываться о нашей архитектуре.

Очереди и блокировки. Теория и практика - 9

Мы нашу архитектуру рассматриваем «под микроскопом» и выделяем две вещи:

Очереди и блокировки. Теория и практика - 10

Первая — какие-то критические элементы нашей логики, которые нужно сделать; и вторая — какие-то медленные и ненужные вещи, которые можно отложить на потом. И эту архитектуру мы пытаемся разделить:

Очереди и блокировки. Теория и практика - 11

Мы разделили ее на две части.

Одну часть мы стараемся положить на один сервер, а другую — на другой. Такой паттерн я называю «хитрый ученик».

Очереди и блокировки. Теория и практика - 12

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

Есть более научное название этого паттерна:

Очереди и блокировки. Теория и практика - 13

И в итоге мы приходим вот к такой архитектуре, где есть веб-сервер и бэкенд-сервер:

Очереди и блокировки. Теория и практика - 14

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

Очереди и блокировки. Теория и практика - 15

Что же такое очередь? Очередь — это список.

Очереди и блокировки. Теория и практика - 16

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

Переходим ко второй части — где и как она используется? Когда-то я работал в таком проекте:

Очереди и блокировки. Теория и практика - 17

Этот аналог Яндекс.Маркета. В этом проекте крутится очень много разных сервисов. И эти сервисы как-то должны были синхронизироваться. Синхронизировались они через базу данных.

Очереди и блокировки. Теория и практика - 18

Как устроена очередь на базе данных?

Очереди и блокировки. Теория и практика - 19

Есть некий счетчик — в MySQL это автоинкремент, в Postgress это через сиккенс реализуется; есть какие-то данные.

Записываем данные:

Очереди и блокировки. Теория и практика - 20

Читаем данные:

Очереди и блокировки. Теория и практика - 21

Удаляем из очереди, но для полного счастья нужны lock’и.

Очереди и блокировки. Теория и практика - 22

Хорошо это или плохо?

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

Очереди и блокировки. Теория и практика - 23

Во-первых, через базу данных можно хранить историю. Тогда добавляется поле deleted, оно может быть как флаговым, так и timestamp писать. Мои коллеги через очередь делают общение, которое через партнерскую сеть идет, они баннеры записывают — сколько было кликов, потом у них аналитика на Вертике находит кластеры, какие группы пользователей на какие баннеры больше откликаются.

Мы же для этого используем MongoDb.

Очереди и блокировки. Теория и практика - 24

В принципе, все то же самое, только используется какая-то коллекция. Пишем в эту коллекцию, читаем из этой коллекции. С удалением — прочитали элемент, он автоматически удалился.

Очереди и блокировки. Теория и практика - 25

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

Дальше, я работал в таком проекте, это социальная игрушка была — «однорукий бандит»:

Очереди и блокировки. Теория и практика - 26

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

Очереди и блокировки. Теория и практика - 27

Я для этого использовал Tarantool. Tarantool в двух словах — это key value хранилище, оно сейчас уже ближе к документно-ориентированным базам данным. Был реализован у меня на Tarantool оперативный кэш, это только чуть-чуть помогло. Решили все это организовать через очередь. Все прекрасно работало, но однажды у нас это все упало. Упал бэкенд-сервер. И в Tarantool начали накапливаться очереди, накапливаться пользовательские данные, и память переполнилась, потому что это Memory Only хранилище.

Очереди и блокировки. Теория и практика - 28

Память переполнилась, все упало, данные пользователей за полдня потерялись. Пользователи немножко недовольны, где-то чего-то играли, проиграли, выиграли. Кто проиграл, тому хорошо, кто выиграл — тому хуже. Вывод какой? Нужно делать мониторинг.

Что тут надо мониторить? Длину очереди. Если мы видим, что она превышает среднюю длину раз в 5 или 10, 20 раз, то должны слать SMS — у нас такой сервис сделан на Telegram’е. Telegram бесплатный, SMS все-таки денег стоит.

Что еще нам дает Tarantool? Tarantool — хорошее решение, там есть шардинг из коробки, репликация из коробки.

Очереди и блокировки. Теория и практика - 29

Еще в Tarantool есть замечательный пакет с Queue.

Очереди и блокировки. Теория и практика - 30

То, что я реализовывал — это было еще 4-5 лет назад, тогда такого пакета еще не было. Сейчас появился очень хороший API у Tarantool, если кто пользуется Python, у них API, вообще заточенный под очереди. Я сам на PHP с 2002-го года, 15 лет уж как. Разрабатывал модуль под Tarantool на PHP, поэтому PHP мне чуточку ближе.

Тут есть две операции: запись в очередь и чтение из очереди. Хочу обратить внимание на эту циферку (0,1 синим на слайде) — это у нас timeout. И, вообще, при подходе к написанию бэкендовских скриптов, которые разбирают очередь, есть два подхода: синхронный и асинхронный.

Очереди и блокировки. Теория и практика - 31

Что такое синхронный подход? Это когда мы читаем из очереди и, если есть данные, то мы их обрабатываем, если данных нет, то мы встаем в блокировочку и ждем, пока данные придут. Данные пришли, мы поехали читать дальше.

Очереди и блокировки. Теория и практика - 32

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

Очереди и блокировки. Теория и практика - 33

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

Очереди и блокировки. Теория и практика - 34

Redis. Это у нас зоопарк, где есть много разных структур данных. Очередь реализуется на списках. Чем хороши списки? Они хороши тем, что время доступа к первому элементу списка или к последнему происходит за постоянное время. Как реализуется очередь? С начала списка пишем, с конца списка читаем. Можно делать наоборот, это не принципиально.

Очереди и блокировки. Теория и практика - 35

Работал я в такой игрушке. Эта игрушка была под ВКонтакте написана. Классическая реализация была, игрушка работала быстро, флэшка общалась с веб-сервером.

Очереди и блокировки. Теория и практика - 36

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

Очереди и блокировки. Теория и практика - 37

Только мой скрипт отрабатывал 50 мс, а когда обращались к внешнему скрипту, там была какая-то Америка, это 250 мс минимум, а то и 2 с лишним секунды ping туда шел. Соответственно, вся игрушка зависла.

Мы применили такую вот схему:

Очереди и блокировки. Теория и практика - 38

И все у нас было хорошо, все работало быстро. Но однажды наш админ ушел в отпуск. Админ ушел в отпуск, все было хорошо первую неделю, а через неделю мы узнали, что Redis течет. Redis течет, админа нет, мы приходим, смотрим с утра на консоль, смотрим, сколько осталось памяти, сколько до swap’а еще осталось, вздыхали: «Ох, как хорошо, пронесло на сегодня». В пятницу к нам пришло много пользователей, особенно после обеда, памяти не хватило.

Очереди и блокировки. Теория и практика - 39

Выводы те же, что и с Tarantool. Другой проект, ошибки те же. Память нужно мониторить. Длину очереди нужно мониторить. Про мониторинг много все говорят, повторяться не буду.

Очереди и блокировки. Теория и практика - 40

В Redis можно также выполнять как блокирующие, так и неблокирующие операции чтения; операция Count нужна, как раз, именно для мониторинга.

Как-то еще я удаленно работал в проекте загрузки видео с популярных видеохостингов:

Очереди и блокировки. Теория и практика - 41

В чем проблема этого проекта? В том, что если мы загружаем видео, то мы его конвертируем, если видео чуть длиннее, веб-клиент просто отваливается. Применили такую схему:

Очереди и блокировки. Теория и практика - 42

Все у нас хорошо. Закачали файл. Но очередь работает у нас только в одну сторону, а мы должны проинформировать наш веб-скрипт — файл-то уже закачан.

Очереди и блокировки. Теория и практика - 43

Как это делается? Это делается двумя способами.

Очереди и блокировки. Теория и практика - 44

Очереди и блокировки. Теория и практика - 45

Очереди и блокировки. Теория и практика - 46

Очереди и блокировки. Теория и практика - 47

Первый — мы через какой-то определенный timeout проверяем статус. Что может быть статусом? Это keyvalue хранилище — лучше уже взять тот же Redis. Key может послужить какой-нибудь MD5 хэш от URL’а нашего. И после того, как мы сконвертировали, мы в keyvalue пишем статус. Статус может быть: «выполнено», «конвертируется», «не найдено» или еще чего-нибудь. Через секунду, через какой-то timeut, скрипт запросит статус, увидит, что выполнено или не выполнено, покажет все клиенту. Все понятно. Это первый способ, мы использовали пулинг.

Второй — веб-сокеты.

Очереди и блокировки. Теория и практика - 48

Очереди и блокировки. Теория и практика - 49

Очереди и блокировки. Теория и практика - 50

Очереди и блокировки. Теория и практика - 51

Мы загружаем файл — это второй способ. Это подписки. Здесь, как раз, использовали веб-сокеты.

Как это делается? Как только мы начали загрузочку, мы сразу подписываемся на канал в Redis. Если там можно было, допустим, memcaсhed использовать или еще что-нибудь, если мы Redis не использовали, то здесь к Redis привязано. Подписываемся на какой-то канал, имя канала. Грубо говоря, тот же MD5 хэш от URL’а.

Как только мы загрузили файл, мы берем и пушим в канал, что у нас статус «выполнено» или статус «не найдено». И сразу же мгновенно у нас Push отдает статус на веб-скрипт. После этого загружаем файл, если он найден.

Очереди и блокировки. Теория и практика - 52

Не совсем напрямую, приблизительно такая схема.

Очереди и блокировки. Теория и практика - 53

Как это делается? Есть некий источник данных — температура вулкана, количество звезд, видимых в телескопы, направленные НАСА, количество сделок по конкретным акциям… Мы принимаем эти данные, и наш скрипт бэкграундовский, который принял эти данные, пушит их в некий канал. Наш веб-скрипт через веб-сокет, обычно используются ноды JS, подписывается на определенный канал, как только там данные получаются, он через веб-сокет эти данные передает на клиентский скрипт, и они там отображаются.

Очереди и блокировки. Теория и практика - 54

Есть такое решение — MamecachedQ. Это довольно старое решение, я бы сказал, одно из первых. Оно было порождено использованием Mamecached и BerkeleyDb, это встраиваемое, одно из ранних, keyvalue хранилище.

Чем достопримечательно это решение? Тем, что используется протокол Mamecached.

Очереди и блокировки. Теория и практика - 55

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

Говоря об очередях, нельзя не сказать о Zerro MQ.

Очереди и блокировки. Теория и практика - 56

Zerro MQ — хорошее и быстрое решение, но это не брокер очередей, это надо понимать. Это просто API, т.е. мы соединяем одну точку с другой точкой. Или одну точку с множеством точек. Но здесь нет никаких очередей, если одна из точек пропадет, то какие-то данные потеряются. Я, конечно, могу на том же Zerro MQ написать того же брокера и его реализовать…

Очереди и блокировки. Теория и практика - 57

Apache Kafka. Как-то я пытался это решение использовать. Это решение из стека hadoop. Оно, в принципе, хорошее, высокопроизводительное решение, но оно нужно там, где есть большой поток данных и нужно его обработать. А так, я бы более легкие решения использовал.

Очереди и блокировки. Теория и практика - 58

Его нужно еще очень долго настраивать, синхронизировать через Zookepek и т.д.

Протоколы. Что такое протоколы?

Очереди и блокировки. Теория и практика - 59

Я вам показал кучу всяких решений. Сообщество IT подумало и сказало: «Чего мы все изобретаем велосипеды, давайте мы все это дело застандартизируем». И придумало протоколы. Один из наиболее ранних протоколов — это STOMP.

Очереди и блокировки. Теория и практика - 60

Его описание покрывает все, что можно делать с очередями.

Второй протокол, MQTT — это Message Queue Telemetry Transport протокол.

Очереди и блокировки. Теория и практика - 61

Он бинарный, в отличие от STOMP, покрывает, в принципе, всю ту же функциональность, что и STOMP, но более быстро за счет того, что бинарный.

Вот наиболее яркие представители, брокера очередей, которые работают с протоколами:

Очереди и блокировки. Теория и практика - 62

ActiveMQ все три протокола использует, даже четыре (есть еще один). RabbitMQ использует три протокола; Qpid использует Q и P.

Теперь коротко об AMQP — Advanced Message Queuing Protocol.

Очереди и блокировки. Теория и практика - 63

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

Очереди и блокировки. Теория и практика - 64

Этот Exchange имеет тип, свойства.

Очереди и блокировки. Теория и практика - 65

Здесь на PHP написано, как его объявлять. Кстати, этот драйвер тоже я разрабатывал.

Очереди и блокировки. Теория и практика - 66

Раз есть ящик отправителя, у нас должен быть ящик получателя. Ящик получателя имеет такую особенность, что мы за одно обращение можем взять только одно письмо. Ящик получателя также имеет имя, свойство. Приблизительно так его надо объявлять:

Очереди и блокировки. Теория и практика - 67

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

Очереди и блокировки. Теория и практика - 68

Этот маршрут определяется ключом маршрутизации.

Очереди и блокировки. Теория и практика - 69

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

Есть второй подход. Мы можем объявить Exchange и с него сделать Bind на очередь, т.е. это наоборот — мы можем с очереди сделать связь на Exchange или с Exchange на очередь, это без разницы.
Очереди и блокировки. Теория и практика - 70

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

Очереди и блокировки. Теория и практика - 71

Наши почтальоны могут быть трех типов:

Очереди и блокировки. Теория и практика - 72

  1. Первый тип — это слепые почтальоны. Они не могут прочитать ключ, они бегут только по тому маршруту, который мы им проложили. Эти почтальоны бегут, и это
    самые быстрые почтальоны.
  2. Почтальоны второго типа могут немного читать, они сверяют буковки, только не знают, как чего… Сверили буковки, что совпадает ключ нашего сообщения и
    соответствующей тропинки, по которой бежать, и бегут по той тропинке. Т.е. наш получатель идет чисто по ключу совпадения.
  3. И третий вид почтальонов — это Topic. Мы можем задать маску, маска такая же, как в файловой системе, и по этой маске почтальончики наши относят письма.

Приблизительно так выглядит, как отправляются сообщения:

Очереди и блокировки. Теория и практика - 73

Какие у нас типичные ошибочки бывают?

Очереди и блокировки. Теория и практика - 74

Типичные ошибочки бывают в том, что люди часто забывают определять связь. Сейчас третий Rabbit — он более-менее приличный, у него есть веб-интерфейс, можно через веб-интерфейс все посмотреть: что там объявлено, какие очереди, какой у них тип, какие там exchange, какие у них типы.

Вторая типичная ошибочка. Когда мы объявляем очередь или exchange, они у нас по умолчанию autodelete — закончилась сессия, очередь убилась. Поэтому ее нужно каждый раз переобъявлять. В принципе, это нежелательно делать, а лучше сделать постоянную очередь и еще назначить durable. Durable — это такой признак, что если у нас очередь durable, то после перезагрузки RabbitMQ у нас эта очередь будет жить.

Очереди и блокировки. Теория и практика - 75

Что можно сказать про RabbitMQ? Он не очень приятен в администрировании, зато его можно расширить, если мы знаем Erlang. Он очень требователен по памяти. RabbitMQ работает через эрланговское встраиваемое решение, но оно очень много памяти ест. Есть некоторые плагины, которые работают с другими хранилищами, но я, честно говоря, с ними не работал.

Очереди и блокировки. Теория и практика - 76

Вот в таком журнале «Системный администратор» я написал статью «Кролик в песочнице» — там, в принципе, то же самое, что я вам тут рассказал.

Очереди и блокировки. Теория и практика - 77

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

Блокировочки

Очереди и блокировки. Теория и практика - 78

Рассказывал я про такой проект в самом начале. Если бы я делал этот проект сегодня, то использовал бы микросервисы.

Очереди и блокировки. Теория и практика - 79

А микросервисы требуют синхронизации как взаимодействия. В качестве синхронизации у нас используется такой инструмент, как Apache Zookeeper.

Очереди и блокировки. Теория и практика - 80

В основу философии Apache Zookeeper лежит znode. Znode по аналогии с элементом файловой системы имеет некий путь. И есть у нас операция создания ноды, создания детей ноды, получение детей, получение данных и запись чего-то в данные.

Znode бывают двух типов: простые и эфемерные.

Очереди и блокировки. Теория и практика - 81

Эфемерные — это такие znode, которые, если у нас пользовательская сессия умерла, то znode уничтожилась, autodelete.

Последовательности — это автоинкрементные znode, т.е. это znode, которые имеют некое имя и числовой префикс автоинкрементный.

Очереди и блокировки. Теория и практика - 82

На примере конфигурации на лету расскажу, как приблизительно это все работает. Есть у нас две группы процессов — группа процессов a и группа процессов b. Процессы 1 коннектятся к процессам 2 и как-то взаимодействуют. Процессы 2, когда запускаются, пишут свою конфигурацию в Zookeeper.

Очереди и блокировки. Теория и практика - 83

Каждый процесс создает свою znode — первый процесс, второй, третий.

И вот мы один из процессов остановили или, например, запустили. У меня тут на примере остановки процессов показано:

Очереди и блокировки. Теория и практика - 84

У нас процесс остановлен, соединение порвалось, znode у нас удалилась, посылается event, что мы слушали эту znode, что в ней одна znode пропала.

Очереди и блокировки. Теория и практика - 85

Очереди и блокировки. Теория и практика - 86

Очереди и блокировки. Теория и практика - 87

Очереди и блокировки. Теория и практика - 88

Посылается event, мы пересчитываем конфигурацию. Все очень красиво работает.

Очереди и блокировки. Теория и практика - 89

Приблизительно так все это синхронизируется. Есть другие примеры, как с бэкапами там кто-то синхронизировал.

Очереди и блокировки. Теория и практика - 90

На этом итоговом слайде я хотел бы продемонстрировать все возможности серверов очередей. Где у нас знаки вопросов — либо это спорный момент, либо просто не было данных. Например, база данных у нас масштабируется, правильно? Непонятно, но, в принципе, масштабируется. Но можно ли масштабировать очереди на них или нет? В принципе, нет. Поэтому у меня здесь вопрос. По ActiveMQ у меня просто нет данных. С Redis могу объяснить — ACL есть, но он не совсем правильный. Можно сказать, его нет. Масштабируется Redis? Через клиент масштабируется, таких каких-то элементов, коробочных решений, я не видел.

Очереди и блокировки. Теория и практика - 91

Такие вот выводы:

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

  2. Скорость, умноженная на надежность. Что я хотел этим сказать? Чем быстрее работает инструмент, тем у него меньше надежность. Но с другой стороны,
    величина этой константы может меняться — это мое личное наблюдение.

  3. Ну и, про мониторинг тут много говорили и до меня профессионалы.

Контакты

» akalend
» akalend@mail.ru

Этот доклад — расшифровка одного из лучших выступлений на обучающей конференции разработчиков высоконагруженных систем HighLoad++ Junior.

Также некоторые из этих материалов используются нами в обучающем онлайн-курсе по разработке высоконагруженных систем HighLoad.Guide — это цепочка специально подобранных писем, статей, материалов, видео. Уже сейчас в нашем учебнике более 30 уникальных материалов. Подключайтесь!

Ну и главная новость — мы начали подготовку весеннего фестиваля "Российские интернет-технологии", в который входит восемь конференций, включая HighLoad++ Junior. Мы, конечно, жадные коммерсы, но сейчас продаём билеты по себестоимости — можно успеть до повышения цен :)

Автор:

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js