Как стать автором
Обновить

Комментарии 62

конечно FIFO, разместил ночью, описался :)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
А по вашему, сообщения, извлеченные из очереди, сохраняются не в БД для дальнейшего отображения пользователю?
НЛО прилетело и опубликовало эту надпись здесь
1. Как связан формат данных и место, где они хранятся?
2. Если правильно выбрать способ хранения, ничего никуда передавать не потребуется.
1) формат данных роли не играет. Хоть XML, хоть JSON, хоть php в text/plain
2) если отсутствует шардинг и одна БД то да. Иначе — см мой комментарий про то, как устроен шардинг.
Хорошо сказано
«а по делу — прикиньте объем передаваемых сообщений в день в соцсети хотябы при десятке миллионов пользователей и подумайте»

а Вы у курсе хотя бы сколько в нашей стране дееспособного населения?

Ну и при таком количестве подписчиков весьма плохая идея — рекомендовать заводить свою очередь на каждого пользователя. Вот об этом действительно стоит подумать.
НЛО прилетело и опубликовало эту надпись здесь
Да да, действительно стоит очень хорошо подумать, когда кто-либо предлагает использовать queue сервер в качестве persistent storage. А еще вдобавок легко оперирует миллионами.

Рекомендую попробовать прикинуть объем на 10 млн пользователей со 100 записями по 100 байт каждая.
А если в 3.14159 раз хуже — то плохая. Поделитесь результатами тестов?
а какую бы модель предложили вы? задача та же — пользователи должны получать обновления от других пользователей. Пусть даже они получают их сразу, а не по крону. Как я понимаю, основная затычка тут в количестве очередей.
Таже задача это какая?
Прежде, чем что-либо предлагать необходимо уточнить детали, и они могут быть очень разными.

Например, я делал системку, которая прокачивала порядка 400 сообщений в секунду с 1000 одновременных коннекшенов, при этом еще писала в базу (и даже по две копии). Написано было на Java. Сам dispatch занимал буквально две сотни строк с несколькими synchronized секциями.

Делал очередь с поллингом просто на базе sql'я, правда с отдельным dispatch процессом, чтобы избежать задержки при обработке входящего http запроса. Тоже вполне нормальное решение, если не нужен максимально быстрый отклик.

Мог бы рассказать подробнее, но наверное здесь не самое правильное место для этого.
можно, даже интересно послушать альтернативы, может идеи сгодятся.
автор топика не против, но если с подробностями и примерами, то лучше отдельным блогом.
на счет по очереди на логин — это правильно подмечено,
но на создание очереди уходит всего 3к, (миллион пользователей — 3Gb) сервера RabbitMQ могут объединяться в кластер, по этому проблем с этим пока нет. честно признаюсь и миллиона пользователей пока нет.

>Ну и при таком количестве подписчиков весьма плохая идея — >рекомендовать заводить свою очередь на каждого пользователя. >Вот об этом действительно стоит подумать.

а что предлогаешь?
«но на создание очереди уходит всего 3к, (миллион пользователей — 3Gb)» —
а сами сообщения хранить в этих очередях не планируется что-ли?

Давайте сразу определимся с некоторыми базовыми понятиями:
— в компьютере нет волшебства или магии
— там есть определенные компоненты (сеть, процессор, память оперативная, память постоянная)
— все ресурсы ограничены определенными параметрами (скорость, объем и т.п.)
— у разных компонентов эти параметры могут отличаться на порядки
(объем 1G RAM или 1T HDD, со скоростью ровно наоборот)

Любая программная система у нас пользуется теми же ресурсами, и если процесс обращается на диск, то следует ожидать большой разницы в скорости, по сравнению с обращением в RAM. И здесь не важно, как называется процесс — RabbitMQ или MySQL. Правда, следует учитывать, что некоторые алгоритмы специально подстроены под обращения к медленным устройствам, а некоторые нет.

Теперь попробуем как-то поточнее описать задачу.
Возьмем для простоты одну машину, на которой стоит база, где записаны 1000 юзеров.
Каждый день 100 юзеров заходят на сайт и отправляют суммарно 1000 сообщений размером 100 байт. Другие юзера залогинившись получают эти самые сообщения согласно тому кто на что подписан. (Тут пока никаких COMET'ов и реалтаймов не рассматриваем).

Рассмотрим первый и самый тривиальный вариант:
* юзер постит сообщение в свою ленту и обновляет для нее один единственный last_post_id,
* подписчики залогинившись глядят на свои last_read относительно last_post_id, выбирают интересующие их сообщения и апдейтят ластриды.

В общем, всё хорошо — данные надёжно сохранены, ключевой индекс строится по «хорошему» полю, он компактный и быстрый, перестраивается только при вставке. Кэш для СУБД у нас есть — диском трясти нет причин. НО!
Трэш начинается при росте количества запростов от подписчиков, так как чтобы узнать что «мне письмо» им приходится каждый раз шерстить все свои ластриды.
Пока данные компактны и влазят в разные кэши, то еще куда ни шло, но чем дальше тем хуже и не понятно, что с этим делать.

см. продолжение

Чтобы избежать лишних шараханий по базе данных по время запроса «читателя» можно апдейтить его ластриды во время добавления сообщения. (здесь считаем, что кнопку «проверить почту» юзер жмет значительно чаще, чем «написать письмо»).
Если делать «рассылку уведомлений» тут же, во время отправки сообщения «писателем», то у него могут возникать реальные тормоза при большом количестве подписчиков. А особенно, если они живут на разных машинах.
При такой схеме, чтобы избежать этой задержки можно пойти на дополнительные(!) издержки, т.е. на самом деле общая производительность может немного упасть, но всё будет более гладко работать для конечного пользователя.

Сообщение от «писателя» не рассылается сразу же, а откладывается в некое специальное место (в очередь) откуда его потом берет worker делает с ним все, что надо, возможно используя при этом другой процессор/диск/компьютер. А это интересно с точки зрения масштабируемости.

Подобная очередь может быть сделана при помощи подручных средств на той же СУБД, но уже нужен отдельный worker-процесс. Реализация «в лоб» на Python/PostgreSQL занимает около 100 строк кода (но это вместе с синхронизацией на несколько worker'ов — тут тонкий момент!) и без проблем прокачивает на обычной десктопной машине пару сотен сообщений в секунду.

Здесь PosgreSQL выступает в качестве «хаба», куда сходятся все соединения и «хранилища очереди», просто потому, что он уже есть, и производительности всей системы достаточно для данных условий.

Но что делать если и этого недостаточно?
см. продолжение
Если всего вышеперечисленного не достаточно, а есть необходимость, например, избавиться от поллинга или от лишнего оверхеда сериализации данных на медленный диск и обратно, то здесь открывается большой простор для маневра и фантазии. Важно только не забывать некоторые вещи, такие как
— если RAM'а не хватает, то все-равно придется писать на диск
— один процессор в одном кванте времени занимается одной задачей
— сериализация/десериализация данных тоже чего-то стоит
— большинство программ значительно лучше выполняют одни функции, под которые заточены и значительно хуже другие.
мне понравились Ваши рассуждения
Очень интересно было бы почитать про реализацию «подписке» на сайте обычными средствами с mysql
вполне возможно
организуется таблица queue в которую пишится все события. Потом очередь читаются и события либо стираются, либо помечаются использованными. на HiLoad проектах обычно ничего не удаляется, просто ставится дополнительный сервер и все…

Вся проблема в обмене данных между шардами, более подробно об этом я уже отвечал в комментариях. Для реализации очереди — ее надо организовывать не в шардовых БД, а в центральной.
знаю, что в некоторых соц сетях используют memcacheq
можно использовать ZMQ, он более быстрый, но не масштабируется. вообще в гуугле много информации про сервера очередей (опенсоурсных и не...).
>Раз уж взялись писать социальную сеть, то пишите все своими руками.
>Иначе как-то непрофессионально получается.
и мускуль тоже написать своими руками?
>Такая очередь крайне просто реализуется обычными средствами mysql.
как показано в статье — так раза в три проще чем и раз в пять быстрее, через мускуль

Мне понравился ответ Дерика: «Раз вы что-то не понимаете, то значить Вам это не надо»
При построении социальной сети по типу шардинга встает проблема обмена данными между шардами.
Можно подробнее с этого места. Какая именно технология используется? Как устроено хранилище данных и почему нельзя использовать репликацию?
Я хочу понять какую именно проблему вы решаете?
что касается шардинга:
не знаю на сколько мне этично рассказывать чужие идеи,
хранилище данных и хранилище файлов — это немного разные понятия и соответственно используются технологии. В данном примере речь шла о хранилище данных. Представляете, что у Вас несколько десятков миллионов пользователей. Все их данные в таблицу на одном сервере не поместятся, по этому данные разнесены по разным БД и серверам.

На одном физическом сервере БД, может находится несколько баз данных. Одна такая единица данных — называется шардом. Например, все пользователи с Id 1..200 000 — это shard1, 200 000 ..400 000 -shard2
Так вот проблема, если у человека 100 друзей и они расположены на 50 разных шардах, и пусть это будет разнесено на 15 разных физических серверах, то для реализации ленты необходимо открыть минимум 15 коннекций к разным БД. Проблему я обрисовал?
[offtop]
а как сделать цитироание?
Спасибо, но осталось пару вопросов:
1 В начале статьи говорится что подписчик _сразу_ узнает о новостях. Потом же, скрипт, формирующий ленту, предлагается запускать по крону. То есть уведомления таки не мгновенные?

2 Опять таки привязано к крон запуску — не будет ли велика в этот момент пиковая нагрузка на сервер? Как вообще с производительностью дела при больших объемах данных? (скажем миллион юзверей, в день формируются порядка 5 000 000 событий)
ответ 1) нет смысла делать ленту Друзей мнгновенной, хотя это реально. Для мгновенности можно реализовать «Эфир». Эфир — реализуется по принципу ленты и ответ будет мгновенным.

2) нагрузка на что? Не забываем, что у нас HiLoad проект, серверов много: сервер БД, сервер очередей, Web сервер, сторадж данных (о нем сознательно умолчено в статье). На сервер БД — нагрузка будет не более обычной, сервер очередей на фронт-энд ни как не повлияет. Если и будет нагрузка, то Пользователь этого не почуствует.
про AMQP на хабре пишете только вы?
вы выступали на хайлоаде?
пишу не только я, на Хабре есть люди, которые разбираются лучше моего
на хайлоаде выступал не я, но я имел приглашение выступить с мини-докладом, который более полно освещен в последних моих двух статьях. К сожалению не смог приехать по семейным обстоятельствам. Дай бог, выступлю на сл. год. Может информация накопится, может будет какой-то и отрицательный результат, эта технология еще мало изучена.
Или пример не удачный, или вы используете инструмент не по назначению. RabbitMQ используется для обмена сообщениями, где кроме надежности требуется еще и низкая задержка доставки. А в примере у вас он — просто persistence для модели publish-subcribe, который можно легко реализовать на нескольких таблицах в БД.

Например, у вас есть пользователь с 1000 знакомых, которые хотят прочитать его события. Когда он делает запись в блог — RabbitMQ придется доставить 1000 сообщений подписчикам (я конечно надеюсь, что он не хранит 1000 экземпляров в памяти). Судя по примеру — скорость доставки вам не важна. Тогда зачем держать очередь на доставку 1000 сообщений, если можно хранить 1 запись о событии и в кроне распихивать уведомления в ленты подписчиков?
частично проблему описал в комментариях выше.
Еще вопрос, к чему абзац о пропуске первых 14тысяч сообщений после отпуска, если сообщения достаются из очереди кроном каждый час?
возможно пример не оч удачный,
это мы планировали, на тот случай — если будет действительно много сообщений.

алгоритм Ленты примерно такой:
-о каждом событии пишется в очередь
— лента разгребается по крону (раз/четыре в день) и формируются индивидуальные данные для каждого пользователя в БД.
— Пользователь видит свои данные, взятые из БД
как вариант: Можно формировать HTML и его вставлять ssi #include

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

Ну допустим, лежат все эти события друзей в каком-то хранилище и мы их выгребаем [по индексам] при чтении ленты.
Выемка данных для френдленты сама по себе тяжелая операция, на которую способ уведомлений не сильно влияет.
проблему шардинга описал выше

>Выемка данных для френдленты сама по себе тяжелая операция, на которую способ уведомлений не сильно влияет.

в том-то и фокус, что не нужно ни какой выемки данных для френд-ленты. Все за тебя делает брокер.
Еще один непонятный момент. В примере вы в кроне выгребаете все события из очереди для конкретного пользователя. Получается, для того чтобы обновить все ленты на сайте нужно пройтись по всем пользователям?? А если их N * 10 ^6?
не вижу в этом проблему. Если мы формируем ленту классическим способом РСУБД, то нам тоже надо будет пройти по всем пользователям. А их десяток, два, три и даже есть сети более пяти… У каждого Пользователя, своя индивидуальная лента и формируется она раз в сутки по крону.
Проходить по всем пользователям не нужно. Нужно проходить только по пользователям, чья лента изменилась.

Модель, которая мне кажется более удачной:

Есть таблица с очередью, в которую заносится событие вида «Пользователь1 сделал запись в блог», «Пользователь2 загрузил фото». Очередь централизованная без шардов.
Есть таблица с лентами пользователей. Таблица разбита на шарды по тому же принципу что и пользователи.
Есть таблица с подписками вида (Пользователь3 читает Пользователя1 и Пользователя2)

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

Плюсы, по сравнению с вашей схемой — нет избыточности в хранении события в N очередях, где N-число подписчиков, не нужно обходить всех пользователей для построения ленты. Доставку сообщения в ленту можно ускорить, если вместо крона использовать демона.
вполне адекватная схема

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

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

какова у Вас нагрузка на очередь.
много вставок в единицу времени — это плохо, а представляете какой объем данных лить в одну БД?

конечно, как вариант снижения нагрузки — все данные со всех шардов сливать в логи, и потом их частями заливать в БД (раз в мин/5)
«много вставок» абстрактное понятие. нужно провести нагрузочное тестирование и посмотреть, сколько конкурентных вставок держит база. на моей практике цифра получилась ооочень достойная, перекрывшая прогнозируемую потребность на порядок.

в принципе, таблицу очереди тоже можно побить на шарды, например по id пользователя, создавшего событие. хотя при этом схема получается по сложнее.
проблема с шардами на порядок все усложняет.
А каков ваш расчетный прогноз и что на практике?
было это достаточно давно, могу наврать сильно. на практике при вставке в 10 потоков получалось около то ли 10000, то ли 60000 вставок в секунду. ни параметров стенда, не конфигурации базы я и подавно не помню. нужно стрелять в конкретно вашу кофнигурацию.
имел ввиду не 60 000, а 6000, конечно
у нас пока проект на стадии внедрения
цифр живых нет, расчетные приблизительно такие же.
А топикастер не пробовал использовать github.com/bkw/php-amqp? Сейчас стою перед выбором что какую именно либу использовать в проекте для PHP, так что конкретный опыт будет очень приветствоваться.
Очевидно топикстартер лох и гуглем пользоваться не умел, по этому ему пришлось изобретать велосипед. Однако, велосипед был по достоинству оценен разработчиками RabbitMQ, что они решили сделать ссылку со своего офф. сайта на его проект.

Ну, а если без шуток, глючная эта библиотека по вышеприведенной ссылке, хотя тоже есть на нее ссылка на офф. сайте RabbitMQ.
>Сейчас стою перед выбором что какую именно либу использовать в проекте для PHP, так >что конкретный опыт будет очень приветствоваться.
если что-то не устраивает в моем расширении, то всегда можно его доработать. А если есть желание, могу включить в проект Разработчиков. На то он и опенсоурс, что каждый может сделать посильный вклад в общее дело.
О мисье так же является автором code.google.com/p/php-rabbit/? Мое уважение!
Долго не отвечал, потому что php-amqp хотел погонять. Что могу сказать — при каких то условиях (которые я даже могу четко воспроизвести), на публичном тестовом сервере и правда выдает эксепшен. Но, с RabbitMQ я знаком второй день, так что может это штатное поведение… Не готов пока обсуждать ее стабильность…

Теперь почему я смотрю в сторону php-amqp, а не в сторону вашей… Ответ очень прост — я не достаточно крут :) Как вы правильно сказали, это опенсорс. С его проблемами… Обе библиотеки достаточно молоды и не имеют большого комьюнити. Обе, потенциально могут принести проблемы на продакшене. А если проблемы будут, то я исхожу из того что фиксить их придется силой команды (автор любой либы тоже человек — ушел в отпуск или просто влюбился… :) А копаться в PHP коде, лично мне будет проще, чем отлаживать сишный код, в котором, к моему глубочайшему удручению, я не могу назвать себя спецом.

Кроме того вопрос инсталляции — у нас достаточно много серверов, компилить клиентов на каждом конечно можно… но не хочется. :)
Так что, если обе библиотеки дадут мне примерно одинаковый уровень стабильности, то я предпочитаю использовать ту что мне будет проще поддерживать.

Однако тесты я еще не закончил и выбор еще не сделал. В любом случае — большое спасибо что сделали альтернативу!

Если же вас интересует мое мнение в общем, то я считаю что ваша библиотека более перспективна. Делать обертку над оттестированным кодом, гораздо более разумно что реализовывать протокол руками (как это сделано в php-amqp). Конечно же и вопрос скорости говорит в пользу вашего решения. Так что единственное что меня беспокоит в текущей реализации, это потенциальные проблемы с поддержкой. Если ваше расширение попадет в стандартную поставку (читай в порты BSD и репозитории) я буду только счастлив его использовать чаще. Если чем то смогу сделать в этом плане — буду рад помочь. Пожалуйста продолжайте развивать этот проект! :)

По поводу проекта чуть позже напишу в личку — хочу протестировать обе библиотеки.

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

что касается дистрибуции: я не сисадмин, но наш сисадмин готовит пакет дистрибуции, который развертывает на продакше. В пакет входят скрипты дистрибуции, всевозможные патчи и пр…
При добавлении нового сервера, запускаем единый пакет дистрибуции и через 10 мин прописываем центральный конфиг и сервер уже в боевой работе.
в том числе и всевозможные расширения, это не единственное нестандартное расширение. которое приходится использовать.
а можно скинуть в личку, что за проект и где собираетесь это использовать?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории