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

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

ну и главная проблема — это обязательность JS для статичного контента.
разве не легче тогда использовать xslt? и просто отдавать статичные xml-данные + персонализированные в 1ом xml`е, а с клиентской стороны браузер сам отрендерит их как надо.
Давно все браузеры стали одинаково и полноценно рендерить XSLT?
с поддержкой xslt у них явно получше, чем с поддержкой CSS
Так косячат же зараза. ИМХО, рано ещё отдавать рендеринг браузерам, хотя это было бы замечательно.
какие именно косяки мешают тебе жить?
Мне, например, помешало, когда фф молча падал при попытке рендеринга. Методом исключения выяснилось, что ему не нравился тег <br/> в таблице стилей. Сам по себе баг не страшный, но после него осталось ощущение, что как-то с поддержкой xslt в браузерах всё странно…
никогда с таким не сталкивался. видимо уже пофиксили давно.
Вы используете xslt на стороне браузера в живых проектах и у вас никаких проблем с рендерингом не возникает?
Если да, то, имхо, было бы здорово написать об этом статью. Потому что техника очень привлекательна, но как-то плохо документирована — статей толковых нет, производители браузеров её особо не рекламируют и ходят мифы о том, что это некроссбраузерно и ненадёжно.
использовал пока проект не накрылся.

ну вот например статья: habrahabr.ru/blogs/css/94139/
кстати, нет, он ещё жив о_0"
mojura.110mb.com/
Тут будут очень простые преобразования, так что справятся. JS тоже по-разному работает, но как-то изворачиваемся же?
>Давно все браузеры стали одинаково и полноценно рендерить XSLT?
В объеме необходимом для выполнения указанной задачи:
MSIE — с версии 5 (10 лет)
FireFox — c рождения (6 лет)
Safari не позже 2006
Опера — с версии ~8.57 (4 года)
Google Chrome — c момента рождения

Мнение:
ифреймы — зло
кэшировать полный html в memcached — зло
кэшировать в браузере пользователя — отлично, но не для краулеров. Если сайт закрытый от ботов — можно забить на это. Если не закрытый — надо мозговать.
Бенчмарки показали, что в некоторых случаях SSI тоже зло. Когда код хорошо оптимизирован, то основная трата времени это установление коннектов к бекенд серверам.
И согласен с amarao, обязательность JS тоже на руку не сыграет.
Коммент весьма категоричен. А есть что предложить в качестве альтернативы?
для HTML кэшей — диск
вместо iframe — вызов JS на onload или просто вставка JS кода
Диск, не всегда подходит. Хотя для больших кусков или страниц целиком, если бизнес логика позволяет — отличный вариант. Тот же akamai.

Но, некоторые блоки, очень быстро инвалидируются, например — последние коментарии на главной странице и прочее.

ну и по поводу ифрейма я ниже отписался :-)
Мое имхо, HTML кэши вообще не нужны. Грубо говоря они пригодны только в одном месте. И модифицировать их намного тяжелее.
Ребята, 21й век на дворе. Сгенерить html из данных это тысячные доли секунды. Может конечно и есть случаи. Но я их (слава богу?) еще не встречал.
ну что же, видимо никакими нагруженными проектами вы не занимались
поздравлять вас?
скоропостижные выводы
Альтернатива — такие же костыли со своими плюсами/минусами. Ждем html5. Или слушаем, что вещает Techmind :).
Ты очень категоричен, Саша :-)
отвечаю по пунктам.

>ифреймы — зло
о, господи, чего вы к этим ифреймам привязались. Это всего лишь транспорт. Используйте <script></script>, input hidden или что-нибудь еще

>кэшировать полный html в memcached — зло
это не правда, и это факт. В зависимости от задачи, иногда очень удобно и правильно кешировать именно представление, а не данные

>Бенчмарки показали, что в некоторых случаях SSI тоже зло.
статья не об SSI. А о CSI кешировании, его можно использовать в паре с SSI, кешируя те самые темплеты. А можно вообще не использовать it's up to you.

А по поводу коннектов — при изменении одного блока всего 3 запроса (на главную страницу, на общий транспортный ифрейм, и к этому блоку данных).

>это обязательность JS для статичного контента
Если сайт полностью статический — это другой вид задачи, и другой вид кеширования. Хотя я не много таких в последнее время видел. Покажите мне такой сайт — я скажу как его правильно кешировать.

Предложенный мной вариант это не «серебрянная пуля», коих к счастью не бывает, а вариант, который может подойти определенным сервисам
>о, господи, чего вы к этим ифреймам привязались. Это всего лишь транспорт. Используйте ><script></script>, input hidden или что-нибудь еще
C скриптом согласен, но тогда возникнут проблемы блокировки загрузки страницы. Интересно, как можно input hidden кэшировать на стороне пользователя :D.

>это не правда, и это факт. В зависимости от задачи, иногда очень удобно и правильно кешировать >именно представление, а не данные
Приведи примеры. Ты меня еще в прошлый раз не убедил.

>А по поводу коннектов — при изменении одного блока всего 3 запроса (на главную страницу, на >общий транспортный ифрейм, и к этому блоку данных).
Согласен.

>Предложенный мной вариант это не «серебрянная пуля», коих к счастью не бывает, а вариант, >который может подойти определенным сервисам
Угу, но ты ведь просил мнение. Получите, распишитесь ;).

Общая идея хороша, но нормальную реализацию можно будет только с html5 сделать :(. Все остальное — костыли над несчастным хтмл. Мое грубое ИМХО.

>C скриптом согласен, но тогда возникнут проблемы блокировки загрузки страницы.

ну можно колбек на window.onload повесить, тогда не будет. Да и там же не данные, а список урлов приходит. Не должно быть много

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

Кстати, не совсем понимаю зачем ждать html5, т.к. это решение будет работать и с html 3.2
да, кстати, по поводу ифреймов. Почему они могут быть тут уместны.

Для краулеров, которые в большинстве своем JS не обрабатывают сейчас ты можешь вернуть
Iframы с контентом (ифреймы видимые, чтобы в клоакинге не заподозрили), после этого джаваскриптом их сделать невидимыми для пользователей. И тем же джаваскриптом рассовать данные по странице.

Т.е. можно экспериментировать, чтобы сделать дружелюбной для сео.
Хм, разве краулеры стали считать iframe и саму страницу одним единым? Насколько я помню, то как раз наоборот. И некоторые приемы SEO применяют перевод контента в ифрейм, чтоб убрать мусор со страницы.
нет, как одну страницу они не считали и не будут считать, наверное никогда, и наверное, это правильно. Но они могут считать это как разные страницы, т.е. каждый ифрейм как отдельную страницу, и соцно можно индексировать этот контент
лучше ссылку вставлять — тогда если яваскрипт по каким-либо причинам не сработает пользователь сможет пройти по ссылке
а что будет по этой(-тим) ссылкам? сырые данные, скажем, в джейсоне?
хороший повод не использовать жсон ;-)
НЛО прилетело и опубликовало эту надпись здесь
Если гиганты что-то используют ифреймы, это не значит что это круто и его можно использовать.
У фейсбука яваскрипта на 1мб, который грузится сразу же. Так что, теперь можно забить на размеры JS?
к сожалению на медленном канале это будет фэйл
алсо задержка может привести к «а вот и страница… так, стоп, где текст??!!!»
но это конечно решаемо через всякие картинки Loading..., но всё равно под вопросом
и однако если нагружать JS ом, может стоит делать Rich Web Application?
А когда я через GPRS интернет (телефон подключен к ноуту) захожу на maps.google.com у меня тоже карты не сразу открываются и долгое время неотрисованные полигоны висят. И я с этим живу :-) Точнее когда-то жил
В комментариях написали про то, что SSI это иногда зло. Зло или не зло, не скажу, но вот в чем лично удостоверился несколько месяцев назад. wget'ом с одного сервера, размещенного в той же стойке, что и другой, получаю с него страницу, для которой Apache подключает для обработки PHP. Происходит это за N-ую часть секунды (повторяю несколько раз, что бы получить что-то среднее). Отключаю обработку PHP — получают уще за M-ную часть от N, т.е. значительно быстрее. Подключаю SSI — снова N-я часть.

И по статье. Идея правильная. Мы на CMS Magazine недавно начали применять такой подход. Самое удобное, что подошло нам — хранить в тех же куках (когда не смертельно, если куки работать не будут) какие-то данные авторизованного пользователя (например, имя, что бы отображать его на страницах: s3.amazonaws.com/floomby/6_9_2010/g3C8tjiqtE2jpkrzVs9HyA.jpg Это позволяет многие страницы кэшировать вообще в статичный HTML.

Ну и используем блочное кэширование редко изменяемых данных.

P.S. Может баян, но, упомянув M- и N-, не могу вспомнить старый институтский анекдот. На военной кафедре, полковник: «Летят M… нет… лучше K самолетов. И оба реактивные».
Очень актуальная тема:

4 веб серверa (apache)
1 база (mysql)
все картинки, видео, музыка на CDN

20M pageviews/month
2M uniques/month

Кэширую все что возможно (query cache, file caching, CDN cache, etc)

при обычном трафике держит все на ура (Avg load: 2-3). Как только пик, все сервера кроме базы зашкаливают (Load: 30+). Nginx поставить неможем.
Если добавить еще один сервер — просто отодвинем пик на время. Незнаю что еще закешировать…
Даже нельзя поставить nginx на все 4 машины перед апачами?
к сожалению нет, связаны по рукам нашим хостером. если поставим nginx то потеряем супорт
Систему мониторинга какую-нибудь используете? Если да — что показывают графики в моменты нагрузки? iowait растет в моменты пиков? Что показывает top, iotop? Внутри сервера аппликухи общаются через unix-сокеты или по tcp?
Похоже на затыки дисков или сокетов.
Внутри все по tcp… я тоже грешу на диски (точнее один диск). все 4 сервера пишут кэш на один nsf mount и затык скорее всего там…
NFS не самый лучший выбор для таких нагрузок. Чем обусловлен выбор одного стореджа вместо нескольких? Может сделать хранение распределенным и не держать все на одном диске?
к сожалению приходиться все на один диск писать. Сложная система кэширования и администрования непозволяет разбивать дату на несколько серверов.

А какую альтернативу NFS предлагаете? GFS?
Тут начинаются догадки, т.к. с сетевыми дисковыми хранилищами не игрался.
Смотрели в сторону GFS, Lustre? Сами диски в каком-либо RAID массиве? SATA, SAS, SSD?
Да и поставьте все таки для начала систему мониторинга. Nagios, zabbix, munin. И определитесь точно, что у вас затыкается.
monit, collectd
можно MogileFS + Perlbal попробовать
У вас или слабые сервера приложений, или не оптимизированный код. У меня:

1 сервер приложений (2 x Quad Core Xeon 2.5GHz, nginx + apache), он же выдаёт статику
1 сервер БД (Dual Core Xeon 3GHz, postgresql)

42M pageviews/month

кэширование в memcached только нескольких небольших небольших но очень нужных кусочков xml и json.
кэширование в файлах html и xml для нагруженых разделов сайта.

Всё работает с большим запасом
> У вас или слабые сервера приложений, или не оптимизированный код
сайты разные бывают,
например ваши 2 сервера будут тормозить при активном использовании 20ТБ базы
Не ваш случай, у вас 4 сервера приложений на один сервер БД
нужно все мониторить, если БД тормозит то нужно не только кешировать но кластер БД сделать, если тормозят скрипты — то критическую часть переписать на C++
дешевле добавить ещё один фронтенд
нужно сначала проанализировать, найти узкие места, а потом уже что-то добавлять. если тормоз в БД, то фронтенд не спасет
я про «переписать на C++»
Одно время мы кэшировали html блоки и полностью страницы на диски. При большой нагрузке тормозило. Отключили кэш — начало летать. Это я к чему? Может у вас там реально большая нагрузка на диски и генерация контента без кэширования пошла бы лучше?
20M pageviews/month -> 670K pageviews/day на пяти серверах?
на 4х… 5й сервер только для базы
Да один фиг, вобщем-то
670K/4 = 170K/сутки на один фронтенд? То есть ~2 запроса в секунду?
это если размазать по времени
в реальности ночью может быть и 2 запроса в минуту… днем пик
«Пик» — это сколько? Вот конкретно, в числовом выражении?
НЛО прилетело и опубликовало эту надпись здесь
Меня терзают смутные сомнения ещё с тех пор, как я прочитал про подобный прием в «техногете». Если в системе предусмотрено внутреннее блочное кешироание на тороне сервера, то не будет ли это «дешевле» по отношению к ресурсам сервера? Ведь в случае кеширования на стороне клиента, мы плодим дополнительные http-запросы, которые сервер всё рано должен обработать. И, очевидно, на пути пользователь — кеш нам нужна программная прокладка, которая будет возвращать этот кеш. Также очевидно, что она не может состоять из одной строки инклуда файла, ведь данные могут быть конфиденциальные (например возле логина пользователя выводится сколько денег у него на внутреннем счету сайта).

В общем, что я хотел сказать. Нужны реальный цифры чтобы оценить выгоду такого подхода.
много запросов только при первом заходе, а далее подтягиваются только изменившиеся блоки.
Допустим. А как узнать изменился блок или нет? На сколько я понимаю принцип работы http-протокола, браузер отправит запрос на сервер и, если получит в ответ «не изменилось», не будет получать данные. Но ведь запрос-то всё равно происходит.

Да, конечно, можно задать время жизни кеша на клиенте — 10 лет, чтобы бразуер даже не спрашивал. Но как тогда обновлять этот самый кеш? Ну вот изменилось что-то, как заставить пользователя очистить кеш браузера?

Тут конечно можно придумать что-то типа версий запрашиваемого кеша. Т.е. в зависимости от чего-то давать разные URL для загрузки. Например "?ver=123564". Но, черт знает, это тоже нужно продумать.
смотри ссылку чуть ниже…
>Допустим. А как узнать изменился блок или нет?

Я же описал в статье как.
У нас есть общий блок ифрейма (неважно чего на самом деле). Который хранит в себе все блоки. Он отвечает за то, был ли какой-то из блоков изменен. У меня в статье это скрипт all_blocks_data_urls.php По запросу от пользователя смотрится его session_id или user_id и проверяется ключ в мемкеше для него. Это всего лишь флаг в мемкеше. Он не хранит данные, нас интересует только наличие ключа. Если ключ есть — возвращаем 304, и все больше никаких HTTP запросов не будет.

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

При запросе к странице, после изменения, мы обращаемся к скрипту all_blocks_data_urls.php и он уже не возвращает 304 заголовок, а генерирует новый ответ (отдает урлы неизменившихся блоков и генерирует 1 новый для изменившегося), при этом в мемкеше выставляются снова два флага (общий, и новый урл), сами данные не хранятся!

Все урлы для блоков возвращаютсяс Expires в будущем, т.е. там запросов больше не будет.

И того, если у пользователя ничего не менялось, то это 2 запроса — первый к главной странице (там кстати тоже можно сделать кеширование на Etag или Last-Modified), второй к блоку, который вернет 304

Если что-то менялось, то запросов 3: главная, фрейм-контейнер, запрос к блоку, который изменился.

Здесь как раз происходит экономия на запросы к серверу и мы его никак не перегружаем, более того, даже память зря не используем.
зачем нужна эта главная «страница»? что мешает совместить её со списком урлов?
ну потому что урлы для данных в блоках у каждого пользователя свои, а шаблон главной страницы у всех пользователей общий. Поэтому и ввобился общий контейнер ифрейма, который отвечает за кеширование.
что мешает грузить «раскладку старницы» в качестве такого же «блока» как и остальные?
ну блоки они чисто логические, а пользователю-то один шаблон возвращается (вся страница). Который должен быть заполнен.

Не нужно пытаться смешать данные и представление, им хорошо друг без друга
зачем запрашвать шаблон _каждый раз_?
ну запрашивать нужно, с if-modified-since или etegом, естественно, и точно также получать 304, пока не произошел редизайн
у вас как часто делается редизайн, что нужно ревалидировать шаблон при каждом заходе на страницу?
я написал, про 304 заголовок при запросе к странице.
Или вы и на основную страницу предлагаете ставить экспаер в далекое будущее о_О? Здесь это точно не уместно
я предлагаю грузить эту «основную страницу» также как и все другие блоки =_="
так, главная страница, и так содержит все блоки. Фактически это один большой шаблон. В который входят логические блоки. Данные для которых мы получим после. Т.е. представление для каждого блока оно уже находится на этой основной странице.

грубо говоря
Hello {%username%}!

{% for friend in friends_list %}
{%friend.name%} - {%friend.lastname%}
{%endfor%}




т.е. когда запросили страницу главную. получили что-то вроде такого. А данные для block1 и block2 уже подтягиваем отдельно.
я это понял уже давно. ты меня принципиально не хочешь слышать? зачем нам каждый раз проверять не изменился ли этот долбанный шаблон?
а как часто нужно проверять? о_О по большему счету такая проверка — это мелочь.

Я тоже понимаю, что ты имеешь в виду, а именно, поставить и на главный долбаный шаблон — Expire.

Но мы не можем так сделать, т.к. чтобы экстренно обновить этот шаблон у всех (скажем, поменять лейаут, забрендировать сайт под рекламную компанию и прочее), то нам прийдется добавлять какой-то гет параметр url?version=123. А это плохо для SEO, т.к. основные внешние страницы должны иметь постоянные урлы. Да и вообще нетолько для СЕО, просто плохо, т.к. некоторые и руками урлы вводят.
Сорри, я протупил. :-) Еще раз весь тред прочитал. Как-то я пропустил это

>я предлагаю грузить эту «основную страницу» также как и все другие блоки =_="

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

Извини, что-то я запарился :-)
ну наконец-то! \(^_^)/
<html>
<body>
<div id='block1'>
   Hello {%username%}!
</div>
<div id='block2'>
    {% for friend in friends_list %}
        {%friend.name%} - {%friend.lastname%} 
    {%endfor%}
</div>
</body>
</html>


Извиняюсь, не посмотрел предпросмотр, перед отправлением
Зачем хранить флаги в мемкеше, если их можно хранить в сесии?
а сессия где хранится?
независимо от того где она хранится: файлы, база, мемкеш, или просто куки
да, можно и в сессии
НЛО прилетело и опубликовало эту надпись здесь
Ссылка на профиль пользователя? ;)
тогда url нужен,
«генерируемый» id может потребоваться если выводиться список пользователей, но и в таком случае лучше не username а user_id использовать,
по моему автор просто привел пример, а не кусок реального проекта
Да, возможно. Хотя, может username = user_id? И генерируется url на основе div.id
НЛО прилетело и опубликовало эту надпись здесь
Данный пример можно написать так, суть примера не измениться:
Hello {%username%}!
чтобы быстрей искать, если данные прийдут в формате ключ-значение.
чтобы не регекспом колбасить, а делать getElementById по айдишнику этого дива.

Но это уже реализация темплетного движка
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Привет, Саша

Да, помню, общались.

>можно вопрос — а не проще ли тогда полностью javascript templates & json data «метод» использовать так сказать?

Если ты имеешь в виду различные JS темплеты типа
code.google.com/p/trimpath/wiki/JavascriptTemplates
или
code.google.com/p/embeddedjavascript/

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

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

Если ты используешь SSI или ESI, то там ты вырезаешь из запроса кусок с SESSIONID или user_id, для него генеришь блок представления, и весь этот блок кладешь в свой мемкеш сервер. Если твой сайт — социальная сеть, то там таких блоков будет очень много: друзья, образование, карьера, стена, любимые места и пр. Так вот, каждый такой блок ты хранишь для каждого юзера. Притом хранишь не только данные, а еще и кучу повторяющегося хтмла. Ну и рано или поздно сталкиваешься с проблемой памяти, а также с проблемой постоянно выпадающих серверов мемкеша, если у тебя их over 9000

Да, сама идеология другая, это не SSI. Просто похожая идея перенесенная на Client Side
НЛО прилетело и опубликовало эту надпись здесь
ну в любом случае у нас так получается, что где-то этот кеш хранится, либо оперативная память, либо дисковый кеш.
В случае памяти, сталкиваемся с проблемой ее размера. В случае диска — дисковые операции медленные. Прийдется делать систему, которая использует что-то совместное. Блоки, которые долго можно хранить — на диск, те которые быстро инвалидируются в мемкеш.

Проблемы именно в размерах, не так остро в скорости, нгинкс быстрый.

А в варианте, который я описал. Мы вообще ничего не храним на серверной стороне. Сгенерировали данные и отдали. Пользователь получил УРЛ с экспаером. И они уже в браузерном кеше. Инвалидируем посредством предоставления новых урлов. Все данные у клиента хранятся.

Или я не совсем вопроса понял? :-)
НЛО прилетело и опубликовало эту надпись здесь
Я надеюсь, у Вашего кусок кода, возвращающего «304 Not Modified», в реальности совсем не такая логика?
я же написал какая у него логика. Проверить изменились ли блоки, если нет — 304, если да — сгенерить список урлов. А что не так с этой логикой?
«Не так» то, что Вы принимаете решение о выдаче 304 на основе внутренних данных сервера. А выдавать 304 можно только если клиент сделал conditional request — то есть послал GET с соответствующим заголовком (например If-Modified-Since как самый распространенный). И только если условие не выполняется, сервер имеет право ответить 304 (см RFC 2616 например).

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

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

Так вот: если кеш отключен или по каким-то причинам был «забыт», то заголоавки If-Modified-Since или If-None-Match отправляться не будут, и соответственно мой скрипт не вернет для этого запроса ответ 304.

Да, вы правы, я не указал этого в коде статьи, чтобы не перегружать код большим кол-вом деталей. Но там должна быть еще проверка анализирующая заголовок f-Modified-Since или If-None-Match чтобы избежать ситуации, описанной вами.
А, ну отлично, именно это я и имел в виду. В таком случае я за Ваш проект спокоен ;)
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории