Обновить
-24
Maxim Penzin@maxp

Пользователь

13
Подписчики
Отправить сообщение
Чего ему загибаться-то? Нагрузка-то по сути никакая.

Было дело, писал аналогичный функционал на Java.
1000 коннектов держало спокойно, причем это даже в режиме «one thread per client».
Багрепорт:
экстеншен поставился, но не проявляет никаких признаков жизни, т.е. вообще.

Gentoo Linux, свежий chromium.
Если всего вышеперечисленного не достаточно, а есть необходимость, например, избавиться от поллинга или от лишнего оверхеда сериализации данных на медленный диск и обратно, то здесь открывается большой простор для маневра и фантазии. Важно только не забывать некоторые вещи, такие как
— если RAM'а не хватает, то все-равно придется писать на диск
— один процессор в одном кванте времени занимается одной задачей
— сериализация/десериализация данных тоже чего-то стоит
— большинство программ значительно лучше выполняют одни функции, под которые заточены и значительно хуже другие.
Чтобы избежать лишних шараханий по базе данных по время запроса «читателя» можно апдейтить его ластриды во время добавления сообщения. (здесь считаем, что кнопку «проверить почту» юзер жмет значительно чаще, чем «написать письмо»).
Если делать «рассылку уведомлений» тут же, во время отправки сообщения «писателем», то у него могут возникать реальные тормоза при большом количестве подписчиков. А особенно, если они живут на разных машинах.
При такой схеме, чтобы избежать этой задержки можно пойти на дополнительные(!) издержки, т.е. на самом деле общая производительность может немного упасть, но всё будет более гладко работать для конечного пользователя.

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

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

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

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

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

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

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

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

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

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

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

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

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

Мог бы рассказать подробнее, но наверное здесь не самое правильное место для этого.
Да да, действительно стоит очень хорошо подумать, когда кто-либо предлагает использовать queue сервер в качестве persistent storage. А еще вдобавок легко оперирует миллионами.

Рекомендую попробовать прикинуть объем на 10 млн пользователей со 100 записями по 100 байт каждая.
Хорошо сказано
«а по делу — прикиньте объем передаваемых сообщений в день в соцсети хотябы при десятке миллионов пользователей и подумайте»

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

Ну и при таком количестве подписчиков весьма плохая идея — рекомендовать заводить свою очередь на каждого пользователя. Вот об этом действительно стоит подумать.
Есть довольно приятная программка Chameleon Clock — www.softshape.com/cham
Там многое из вышенаписанного реализовано уже лет 10 как.
Кстати, софтинка не буржуинская, как это может показаться, а совсем напротив — разработана в Иркутске.
Вот-вот, слава уродского языка останется за ПХП навсегда.
Концептуально!
У меня было вообще забавно. Схема такая — страница при загрузке шлет серверу «я тут», а сервер регистрит её и начинает в ответ слать данные (у юзера машинка по карте начинает ездить). Все было зашибись пока не пришлось добавить один маленький инит-пакетик перед потоком данных. Выглядит всё здорово — сервер получает «я тут», высылает «инит», его видно в tcpdump, он проходит по логам XHR, но на страницу не попадает, сцуко!

Пришлось вставить reset_queue(), который очищает клиентскую очередь пакетов на сервере и засылает туда для начала «nop», а потом уже «инит» и далее по списку.

«nop» пакет вообще полезен, у меня сервер с интервалом в несколько секунд высылает клиенту «nop» (там еще и серверное время вставлено на всякий случай), а на клиенте поставлен таймаут на XHR побольше, чем на сервере, чтобы ни там ни сям не клинило и всякие NAT'ы не протухали.

На самом деле можно придумать множество методов, чтобы доставить все нужные данные.

Я просто рассказывал про достаточно не очевидный момент с зомби-XHR, возможно это сэкономит кому-нибудь кучу времени.
Пару лет назад реализовывал подобный мультиплексор на Java. Благо там реализация синхронной очереди делается в пару десятков строк, достаточно эффективно и надежно. Несколько сотен запросов в секунду не создают тормозов.

Хочу обратить внимание на один достаточно не очевидный момент:

Представим себе страницу с чатом, которая получает новые сообщения при помощи ajax, как написано выше,
т.е. потом сообщений проходит через мультиплексор, который сгружает их в клиентские очереди, откуда их выгребают при помощи функции getData(), как описано выше.

При F5 на этой странице имеем реальную возможность потерять сообщение!
(Ладно бы это какой-нибудь чат, у меня стабильно терялось init сообщение :)

Дело в том, что живой XmlHttpRequest заблокированный на сервере не умирает в момент релоада страницы,
и следующее сообщение он вычитает из очереди и доставит, правда уже в никуда, так как его контекста уже нет.
«Если не описаться» => «Если не опираться»

Старина Фрейд видимо уже переворачивается в гробу :)
Целиком и полностью присоединяюсь к точке зрения, что использовать какую-нибудь технологию следует только понимая для чего она предназначена.

JSON интересная мысль, рекомендую послушать рассказ автора про то, как «я стал стандартом» :)
Ссылка была где-то на yahoo developers. И вообще, Даг очень импозантный и грамотный пипл.

Кстати, по поводу JSON'а в народу ходят мысли, что неплохо было бы его сделать в формате multiple documents in file, т.е. по сути вычитывать тривиальный список JSON объектов (одна строка — один документ), но с возможностью зкомментить (!) какой-либо из них.

Если не описаться сильно на Javascript, то в этой ситуации неплохо выглядит YAML, он даже покомпактнее будет.

Если говорить о бинарных сериализаторах, то можно взглянуть на Hessian.
А когда действительно нужна максимальная скорость, то видимо google protobuf тут вне конкуренции.
У меня тоже ломалось. Плагин недавно обновлялся, возможно уже починили.
«Второй вопрос, а зачем он нужен вообще?» — вот этот вопрос стоило бы переадресовать твиттер-пользователям — пусть каждый сам решает зачем ему микроблоггинг. А как ответполучился несколько притянутый за уши, особенно по части динамического IP.
Какой-то странный эксперимент, однако — пробовать выковыривать что-то из собранной и заточенной системы. В таких случаях гораздо правильнее использовать обратный путь.

У меня, например, в Gentoo особой зависимости между Gnome и XFCE не обнаружено, а еще можно просто в первоисточники взглянуть — www.xfce.org/documentation/requirements.

Везде явно прописаны gtk+ и glib, но это ну никак не gnome environment.
«Менеджер окон сделан на Gnome» и далее XFCE4 как-то не очень сопрягаются.

По всей видимости оконный менеджер там xfce, а интерфейс на gtk виджетах.

Информация

В рейтинге
5 966-й
Откуда
Иркутск, Иркутская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Фулстек разработчик
Ведущий
Python
PostgreSQL
Linux
Java
MongoDB
Redis