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

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

Решение заслуживает внимания, но вы все равно дергаете JavaScript-ом данные, пусть они теперь и не в БД. Для старых браузеров лучше оставить классическое решение, которое Вы привели в начале. Тем более, что новости не информация «реального времени». Для новых используйте WebSocket, я думаю скоро все асинхронные операции через эту технологию будут проходить.
Мне кажется тут уместнее использовать Server-Sent Events.
Я думал о WebSocket. Но технология пока сыровата. В Firefox например ещё MozWebSocket. Да и серверная часть должна быть соответствующей. Но сама технология конечно крута, тут я не спорю
Посмотрите в сторону socket.io, я недавно делал на них мониторинг изменений в .log файле. Т.е. как только в лог добавлялась новая строчка, событие сразу перехватывалось, эмитило эту строчку всем подключенным клиентам и даже моргало лампочкой на Arduino :).
Реализация — несколько строчек кода, работало у меня даже на e-ink читалке kindle 4, не говоря про остальные браузеры.
Спасибо, гляну )
У WebSocket один минус: спецификация ещё не устаканилась, и время от времени выходят новые версии протокола, и при выходе новой версии браузеров, что-то может ломаться.
Да. Текущая версия 07 например добавила обязательное маскирование всех сообщений. Я пока не находил готовое решение которое поддерживает этот драфт
Помом текущая уже 10
socket.io, это несколько больше, чем WebSocket.
В зависимости от браузера socket.io выбирает технологию для обмена сообщениями, заявлена поддержка:

WebSocket
Adobe® Flash® Socket
AJAX long polling
AJAX multipart streaming
Forever Iframe
JSONP Polling

при чем сам socket.io выбирает технологию автоматически.

Supported browsers

Desktop

Internet Explorer 5.5+
Safari 3+
Google Chrome 4+
Firefox 3+
Opera 10.61+

Mobile

iPhone Safari
iPad Safari
Android WebKit
WebOs WebKit

и это не просто голословное заявление, оно действительно работает в разных браузерах. После того, как у меня сокеты заработали в примитивном браузере e-ink читалке, у меня сомнений не осталось.
Да, я знаю, ошибся уровнем комментариев :-) Впрочем, оттого, что socket.io не сможет найти поддержку WebSocket из-за того, что браузер работает с более новой версией протокола, и перейдёт на long-pooling или флэш мне не легче)
Отчего же не станет? Судя по всему этот модуль достаточно активно развивается, так что такой режим совместимости будет временным, после обновления все будет работать нативно.

Для критичных задач можно продублировать стандартными методами, но с большим интервалом.
НЛО прилетело и опубликовало эту надпись здесь
Можно считать, что устаканилась. Покрайней мере, выросла из драфта и получила свой рфц
tools.ietf.org/html/rfc6455
Я так понял фишка в том, что как раз и не дергается ничего кроме статики, пока обновлений нет — так что вполне заслуживающий внимания метод. А старые браузеры — это уже другая тема, к тому-же можно юзать не Etag.
Если высоконагруженное что-то по-моему вполне хороший метод.
Да, Вы все верно поняли. Я вот тут подумал, что статика любит попадать в разные кэши. Поэтому нужно внимательно все проверять.
Верно подметили кстати. Надо проверить через всякие прокси. На всякий случай можно htaccess выставить Cache-Control: private; чтобы прокси не кешировали
НЛО прилетело и опубликовало эту надпись здесь
Ход мысли интересный ;)
Осталось теперь сделать главное:
совсем избавиться от дергания ПХП и базы на каждый запрос уже на сами данные.
В моем понимании готовые новости — это уже статика,
ПХП нужен только чтобы их обработать и сохранить для клиентов один раз.

Мой вариант:
вместе со страницей отдаем таймстемп последней новости,
и создаем на сервере пустой файл с именем на основе этого таймстемпа,
в итоге все клиенты периодически пингуют этот файл и получают пустой ответ;
а когда новая новость появилась — записываем ее в этот наш файл (можно в json формате),
и создаем новый пустой файл с именем по таймстемпу последней новости, итд.
Клиенты получили новость, показали и начали пинговать уже следующий файл.
Надо только не забыть повесить крон джоб раз в день чтобы удалять старые файлы.
Что скажете?
Да. Тоже об этом думал ) Интересная идея. Я вижу пока только 2 проблемы всего этого дела. Это большой поток трафика между клиентом(ами) и сервером. И почему-то при обновлении файла иногда onModiefied колбек срабатывает 2 раза с разным Last-Modified временем. Не могу понять в чем проблема (
Хм. Интересно. При интервале в полторы секунды onModified иногда 2 раза срабатывает при одном обновлении файла, а при интервале 3 секунды все впорядке
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
А айдишники новостей итеративны? Можно посылать запрос «дай новости с айдишником больше чем my_last_id». И кешировать ответы на какое-то короткое время (fastcgi_cache), тогда, по идее, этим запросом не заспамят базу даже вручную.
В моем случае я использую предыдущий Last-Modified файла как отправную точку в текущем запросе на получение данных. Т.е. дай мне данные начиная с даты создания больше чем предыдущий Last-Modified
Айдишники предложены с целью максимально увеличить количество идентичных запросов от клиентов. Если нет резона использовать айдишники, то можно попробовать передавать в урле не таймштамп, а, скажем, таймштамп/60. Тогда можно кешировать вебсервером ответы для каждой минуты (или любого другого периода на стороне веб-сервера). Соответственно, база гарантированно не будет дергаться чаще раза в минуту, даже если этот запрос будут дёргать боты.
Я где-то пару лет периодически использую такую схему:
Два статических JSON файла. Один содержит ленту событий отслеживаемую клиентом, второй является хранилищем unix timestamp вида {«fresh»:1328202452}. Клиент опрашивает простым полингом файл с таймстампом, и при его изменении запрашивал основной файл. Вся прелесть этого решения дополняется nginx-ом, который эту статику весьма бодренько отдает. Операции с БД происходят только в момент записи новых данных. В случаях когда данные в БД поступают намного реже чем читаются, данный метод работает наиболее эффективно.
Меня только вот что беспокоит. Все подключенные клиенты, в один момент +- пару секунд начнут ломиться за новыми данными…
Они будут ломиться за статическим файлом, который отдаст nginx. Никакой блокирующей нагрузки, в виде исполнения кода на сервере и обращения к БД при этом не будет. Единственный минус который проявился для такого решения — данные в файле дублируют данные в БД. Но если в файле хранится уже агрегированные данные, например результат запроса в БД с джойнами, то про дублирование уже как-то и не беспокоишься. База не дёргается, данные отдаются с минимальной нагрузкой на сервер — что ещё надо?
Я подумал что после обнаружения изменения клиент уже идет за базой. Если за статикой то да, согласен. Я вот только заметил, что Апач как-то странно мониторит файл. Чем дольше я его не меняю, тем больше потом после изменения файла апачу требуется чтобы поменять ETag и выдать его клиенту… Т.е. некоторое время после изменения файла, апач продолжает отдавать неверный Last-Modified.
Вроде вылечилось параметром cache: false у аякса
Отключение кеша — не самый хороший метод. С кешем не надо бороться, его надо использовать. Но я не в курсе деталей вашей задачи, поэтому может ваше решение вполне рабочее.
cache false заставляет jQuery добавлять таймстамп к урлу. Получается
GET /web/ping/new_news?_=1328733099144
Видимо у апача ещё какой-то слой кеширования, который при одинаковом урле продолжает отдавать некоторое время неактуальные данные.
Я предпочитаю самостоятельно управлять этим таймстампом добавляя в запрос параметр nocache:1328733099144. Но в описанном мною выше решении, я как раз при обращении с файлу с лентой событий подставляю таймстамп полученный из первого файла: {«fresh»:1328202452}. Таким образом я могу снизить реальную нагрузку на сервер в момент коллективного старта о котором вы писали. Основная масса клиентов запросив повторно данные из файла-ленты, при таком подходе, просто получит их из кеша своего браузера.
Эх, ну теперь, подчеркиваю, теперь, обеспечить сервер на лонг-поллинг, очень просто. И пожалуйста, адекватно оценивайте количество _параллельных_ коннектов, которых далеко меньше за посещаемость. Сервер на Node.JS, пара десятков строк, на слабеньком инстансе амазона или обычном сервере (на реальной железке еще лучше работает), обеспечивает до 16К паралельных (то есть ВСЕ одновременнно) сессий, при этом отдавая данные ВСЕМ клиентам раз в секунду или около-того.
Мой пост — это как бы костыльно-временное решение до нормальной поддержки WebSocket. Там уже можно изголяться будет как угодно ) Я думаю nginx будет отадвать статику не медленнее node.js а даже быстрее )
Мне кажется, ставить вопрос о том, кто быстрее раздаёт статику, нода или nginx несколько иррационально.
Так же как и сравнивать мой подход по пингу статики с участием только сервера long polling с участием node.js Это совсем разные подходы. Что лучше-хуже тут трудно сказать. Все зависит от задачи.
Я нечто подобное реализовывал, но все же много проще благодаря pusher.com/
Хм, если уж не нравиться WebSockets/Long-polling, то по-моему проще использовать varnish. К нему есть чудный api через telnet. Загрузили новый материал, почистили кэш по url'у, отдали контент. С трудом представляю более быстрый вариант.
P.S. Я бы использовал socket.io, но это дело вкуса.
Мне кажется автор изобретает велосипед. Точно такую же логику предоставляет использование стандартного HTTP-метода HEAD. Причём трафик будет меньше, т. к. при запросе возвращаются только заголовки. Т. о. логика получается следующая: с помощью HEAD-метода получаем заголовки, в которых читаем, например, параметр «Last-Modified», в котором содержится timestamp последнего обновления новостей. Если этот timestamp отличается от того, что содержится на клиенте, то обновляем timestamp на клиенте и выполняем запрос на получение самих новостей.
Ну тогда уже лучше вспомнить про заголовок If-Modified-Since — не придется выполнять дополнительный запрос на получение самих новостей.
Только статья-то не об этом. Независимо от метода запроса — GET, HEAD — пехепешному бэкэнду приходится стучаться в БД чтобы узнать, есть ли там новые записи. Суть в том чтобы nginx, или апач сообщал о наличии новостей, минуя php и mysql.
кгхм, и зачем вам вообще понадобился этот пустой файл? если вы используете Etag, то вам достаточно отдавать 304 с пустым телом, если ничего не поменялось. сей механизм используется уже давно в Ruby on Rails к примеру.

то, что вы «изобрели» — просто смесь файлового сервер-сайд кеша и этого механизма.
Основная идея в том, что ETag отдает например nginx, без привлечения какой-либо динамики на стороне сервера. Это очень быстро
так сделайте нормальный серверный файловый кеш. как только добавляется запись — сносите файл, в котором содержится настоящий ответ, а не только пустая затычка. и настройте правильно нгинкс. он тогда сам будет проверять наличие кешированного ответа, и если его нет, то запрашивать сервер, который будет класть кеш в нужное место. и nginx сам будет отдавать 304 и пустое тело, если на клиенте свежий вариант.
А если данные user зависимы. К примеру с головы рядом с названием новости — к-во раз, сколько пользователь её просмотрел
Точнее смотреть то он её не смотрел ещ ибо новая, но например может ли он её смотреть вообще в зависимости от прав каких-то там. Новости это только пример, не совсем удачный может
в таком случае в вашем варианте будет n пустых файлов на n юзеров. ровно столько же, но не пустых файлов, а полноценных кешей будет в моём случае.
и вообще, Use the Redis, Luke!
У меня есть пару замечаний по js коду. Я бы посоветовал отказаться от функции setInterval (), в сторону setTimeout(), здесь написано почему shamansir.github.com/JavaScript-Garden/#other.timeouts. Вместо того, что бы объявлять во всех функциях переменную me, и присваивать ей this, можно объявить ее в конструкторе. А так как используется jQuery использовать $.proxy, в $.ajax передавать this через параметр context, и тогда эта переменная вообще будет не нужна.
достаточно выставить в $.ajax время таймаута соединения, и от setInterval можно не отказываться, и заодно не западать на лапшу коллбеков с setTimeout.
Лучшее решение из виденных — socket.io — но его пришлось допиливать напильником как в самом коде, так и (особенно тщательно) server-side. Были нюансы с «пробросом» портов, https-соединением и т.д.
Сам socket.io был допилен так, чтобы избежать по-максимуму использование флеша для «сокетов», и разное по-мелочам.

В результате механизм работает с Safari, Chrome, FF, IE7+ (вот за 6-й не увверен, хотя по идее должен работать), Opera.

Единственное где не работает — Opera Mini — но это из другой оперы…

(NB: допилена была не самая свежая версия socket.io — 0.6.2 Не исключаю, что в последний 0.8 — уже многие проблемы исправлены)
Скажите, а socket.io вы использовали с Node.js? Или есть полноценные варианты и для других платформ?
github.com/learnboost/socket.io/wiki/

In other languages

Erlang
yrashk/socket.io-erlang
Java
ibdknox/socket.io-netty
benkay/java-socket.io.client
Ovea/Socket.IO-Java
Gottox/socket.io-java-client
Lua
ignacio/LuaNode-Socket.IO
Perl
vti/pocketio
Go
madari/go-socket.io (Currently not compatible with 0.7+)
Python
MrJoes/tornadio2 (0.7+)
MrJoes/tornadio (0.6)
gevent-socketio
django-socketio
Ruby
dkastner/Socket.io-ruby
markjeee/Socket.IO-rack
Flash
simb/FlashSocket.IO

правда, эти все библиотеки написаны сторонними разработчиками, так что о полной совместимости говорить сложно. Надо смотреть в каждом отдельном случае и думаю, по возможности, все же использовать оригинальный node.js
erlang
Но везде приходилось усиленно работать напильником
Есть мнение, что вы тут занимаетесь ерундой.
Всякие сетевые задержки + тормозной протокол HTTP + тормозной веб-сервер сожрут от 90% до 99% времени, как вы не извращайтесь. PHP конечно такой-сякой, но такие элементарные случаи оптимизировать умеет. База данных, какая бы ни была, если данные есть в памяти (а они будут в памяти после первого же обращения) — выплюнет ответ за наносекунды.
Если что-то и пилить, так это архитектуру всего приложения.
Сложно представить, чтобы юзер сидел и втыкал в блок новостей несколько минут подряд. Скорее всего он либо переключится на другую вкладку, либо будет что-то там делать, переходить по каким-то ссылкам и т.п. Вешайте ваши запросы на апдейт новостей на эти лишние запросы. Таймер для «втыкающих» вполне можно выставить в несколько минут.
Ответил ниже )
Суть поста вообще оставить в покое PHP и Базу до наступления новых данных. Новости тут только к примеру. Не нравиться, не занимайтесь ерундой, проходите мимо
К этому костылю будет полезно прикрутить логику увеличивающихся интервалов. Не получили новость за секунду, умножаем на два и ждём две секунды, не получили за две — ждём 4. И так далее, до верхней границы, которую вы сами себе определите. Например, пинг раз в минуту. Это значительно снизит нагрузку на сервер.

Часто в подобных костылях применяется следующий подход — с каждым полученным ответом сервер шлёт минимальное время интервала, коэффициент умножения и максимальное время. Например, если сервер видит, что кто-то усиленно комментирует пост, то он может выставить коэффициенты (1, 2, 60), а если комментарии редкие, то (10, 3, 180).

Есть ещё 4 параметр — через какое время прекратить долбиться на сервер. Это делается специально для людей, которые открыли вкладку и пошли спать.
>> открыли вкладку и пошли спать
Мне кажется в таком случает должен сработать window.blur по идее. А на blur я вообще перестаю пинговать
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории