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

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

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

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


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

273aa8a6a43a06678364c473a7a08aa8.png

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

abe8f0ae36a3ce95614259e321e58ade.png

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

7f0322aa988b3c68ac7ec4f152ee9c51.png

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

6e28557c8d6bdb4d6a1b45ecdd57f0a3.png

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

6796a069abb612378da04fcc5da32f57.png

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

142e602494bc6d3c90c2c68f22fbce35.png

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

4a759a3498fa6b666315174a51a8b55a.png

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

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

acb3d0f833e4f020c7c2e44c541b4326.png

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

075344c048dd68d17c561bfd6e333bf9.png

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

d086a62c03ddcdd23aad6bfff6c153c7.png

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

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

8f4cd7b5b61efa5aeab1f2f6b9964e01.png

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

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

58c9a4f741fb00773c87982b03c58f67.png

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

0633d65a4c04bd72d33e973c974522e5.png

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

f335c7ae7091ec54bd8daf511563a6b8.png

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

9612a5566dc622ba94d1c47fc30b09fd.png

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

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

176f8d0f53b1802f68f4cca098fffc22.png

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

9c354e143b7ed61cc4bd1301ffd43566.png

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

fa7d02529295cbad89c1a75fe4027346.png

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

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

f654bf88fae2b4c6ce8f6b626760a66f.png

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

672774ba67531b25b6c2140b6635316b.png

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

d635f0d46046ef7bd4a53a8623d0f76c.png

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

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

b2e7bd4c3ffb2829c3625a7736b166b0.png

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

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

38984abe3ec2fab7e781ef00cde759ba.png

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

6ec3cf66f7d87f00fb86572c4a007ef6.png

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

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

c0033c604f95bc064979f7c13370e550.png

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

b5c7ddd4be03918ce567ae400e6bebcc.png

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

c48d3f64c3da12d63c12135b762e3e3b.png

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

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

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

04c66a93ed7e09eaace0b8d4c1bca8d7.png

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

83c41f5f4c5b65e141bb9bccad8ac4f5.png

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

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

f93c3e48c12a6e0c5e6da1533046e47d.png

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

7a826c22b40b3905950baf60c2608b8a.png

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

1de4cde334cd7b582bd03207fcee73aa.png

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

a99df5ec8d748f604dae276bec91d0c8.png

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

52cb7ecb4439391be5ebd8bbce25c5f0.png

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

5b6ee1605788f91349843240b2a85886.png

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

964dff474fb4013fd786739685548f33.png

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

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

c23faafa6456cfdb489af07e9ffbc66f.png

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

fc261615aff92e726c98e569b03b6bc6.png

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

e7456f6379ca48716bdd6dc4ac805e20.png

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

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

08a751729a6e2a2587076aad54b576f1.png

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

97a4f850418a083ea5e30c2aee915d46.png

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

da0d0c8f448b1dc4f4a5ba9234cb16c7.png

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

75c61d733053f68058e91a125566d2a7.png

85c873810e4f4632541ecdc78aaa15f0.png

5f8a4646d4bd4dab5bf44c96c0995440.png

050edc27e96f0d3ccdfcdb5d23fb3b94.png

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

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

ecebc37f0505f36bdfd43c88957b1c8d.png

c391add03a00c91111a15897a658e833.png

071a0ccc079c2029fff3814df5f3e527.png

5518d1e38763d7d109d84dfed7b02130.png

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

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

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

1869bd6705032d33be65b3df70e3d869.png

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

0bd8c4add19aed0ffe0b6a095fd303e7.png

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

028620c47e23072d906eae6f9a654c96.png

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

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

b513b22c17b32b7a8ae372e070eaa966.png

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

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

693f7f320619c8777f3c47af77a80e3c.png

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

e8270e713f4271ed49e2fe4ee1fb439e.png

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

217937fdc307a3f9913a0954565a858d.png

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

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

b74d8d06bd0c474ef27f504c0a053ff1.png

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

b918cb0e5dd5cc74de8ef79e92dfa128.png

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

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

07e29498fa30f1aed8b85cc9caf60272.png

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

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

6494410163335927b2092fd47cc4aa09.png

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

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

f0333a4fabb463e5a7e8d74fa7a64f32.png

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

dbb854ceca687ed232b2b91ce53fe3e1.png

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

13c6ca261b4c7f74f70c8b82c8033612.png

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

4a1c7f3d71decc823b17020b3ae555d9.png

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

c8ece946f1191ab343c5f8ed5e498d9f.png

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

ef249de2254986635999dd6276000d0b.png

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

d065937545a49cc5ea4d22f1241469e1.png

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

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

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

fcb8287618e1d406aaf13ae3eb483865.png

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

6f432fd043e60203f6a534a42765b596.png

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

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

0013670e6a3cfae5050c36220c4bd8db.png

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

5c8df0bcf45196d8f90e62abb9f8216e.png

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

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

c9ed9788faa1c42ab96c652289c2a8a3.png

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

1420648ab213952f0350c0384e04ca91.png

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

5323986ca0a11a6142afd74148eee283.png

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

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


2454bc616dc7802e48a5617577b5e90b.png

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

f61a06dfdfb3dc3199d5bcabbf7e8b0a.png

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

4045f90bc16da78f1cad61c93caeeb0c.png

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

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

5df37ab506e9884f21fecedac8a39a7d.png

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

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

f5f48d3a2d0bbff5a2cd32ea446332dd.png

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

9c4c20d3bf04d54c4370c68cc79c458f.png

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

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

1a4bbc9dc873bfa6b6a69810c6968c22.png

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

4c4a3fba01cada1d6c6c4573045a7ebf.png

382b74bb08cfdb31452dab186657da29.png

d37bc91d1abda0bb4cfd12bae0ee4bfa.png

9eedaa6e846c4bfda1b5c48af7d3ae32.png

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

3391911b329b1b99bab8e7437aae8194.png

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

3e51795fc3c7ac4fbe68f6ed53037f2f.png

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

7d3c26546c6805bddd660a57d0035957.png

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

  1. Надо каждый инструмент использовать по назначению. Я много общался с разными разработчиками, RabbitMQ сейчас использует только неленивый, но в
    большинстве случаев тот же RabbitMQ можно заменить Redis«ом.
  2. Скорость, умноженная на надежность. Что я хотел этим сказать? Чем быстрее работает инструмент, тем у него меньше надежность. Но с другой стороны,
    величина этой константы может меняться — это мое личное наблюдение.
  3. Ну и, про мониторинг тут много говорили и до меня профессионалы.

Контакты


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

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

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

Комментарии (4)

  • 29 ноября 2016 в 18:42

    0

    В случае реализации очередей на PostgreSQL следует упомянуть про PgQ из SkyTools написанный Skype, для реализации репликации Londiste.

  • 29 ноября 2016 в 20:09

    0

    Вот тоже самое только переработанное под хабр хочется. Сам доклад не то что бы что-то новое хабру даёт.
    • 29 ноября 2016 в 20:18

      0

      Коллега, мы ведь с Вами это уже обсуждали? :)

      У нас нет ресурсов на адаптацию, но есть ресурсы на расшифровку. Материалы хорошие, доклады выбраны пользователями. Мы честно пишем, что это расшифровка доклада, всё по-чесноку!

      • 29 ноября 2016 в 21:34 (комментарий был изменён)

        0

        Олег, приветствую) А не проще давать видео, поделённое на экран с презентацией и самого презентера? Не вижу смысла в расшифровке, которую никто кроме поисковых роботов не будет до конца читать. Можно и сами слайды выкладывать, если уж на то пошло (pdf, ppt)

© Habrahabr.ru