Pull to refresh
2
0
Константин @blackhearted

User

Send message
Возьмите github.com/tarantool/nginx_upstream_module.

Именно его принято использовать при построении сервисов на базе тарантула и nginx-а. И главное — появится возможность управлять конектами к тарантулам в секции upstream и не писать все это самому, ведь в продакшене вы наверняка захотите работать с пачкой тарантулов, чтобы было распределение нагрузки между ними, failover и прочие фишки, которые есть у nginx upstream-ов.
Превратить в ад можно практически любой проект, особенно если инструменты/технологии для его реализации выбраны неправильно и не учитывают дальнейшего роста продукта.
В этом смысле тарантул со своей кооперативной многозадачностью не дает расслабиться с самого начала и заставляет думать о том как параллелить базу и вычисления еще на этапе проектирования.

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

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

Gzip для мобильного телефона важен, т.к. экономит трафик, без него просто нельзя работать. Отличие от js тут в том, что если gzip на телефоне тратит 1мс cpu на inflate, то он и дальше будет тратить 1мс, в отличии от v8 у которого может неожиданно случиться gc.

А вот про вебкит отдельным процессом интересно. Можете показать какой нибудь список работающих процессов (ps ax) где было бы видно ноду и отдельно работающий вебкит? При условии что в несколько потоков к ноде идут запросы от поисковиков, сколько у него там процессов, трэдов и т.п.?
На синтетике TT проигрывает даже сильнее, но в реальности у v8 есть gc, который слегка снижает разрыв.

Шаблон на TT
[%- FOREACH i IN data; i; END -%]


Исходный шаблон на FEST
<fest:template xmlns:fest="http://fest.mail.ru" context_name="json">
    <fest:get name="common">
                <fest:param name="html">
                        <fest:for iterate="json.data" index="i" value="item">
                                <fest:value>item</fest:value>
                        </fest:for>
                </fest:param>
    </fest:get>
</fest:template>


Тестовый скрипт на перле. Берем массив из тысячи элементов и прогоняем его тысячу раз. Все это работает в виртуальной машине на средней по мощности девелоперской тачке.
use strict;
use warnings;

use JSON::XS;
use V8::MonoContext;
use Template;
use Digest::MD5 qw/md5_hex/;

use Time::HiRes qw/gettimeofday tv_interval/;

my $result_tt = '';
my $result_fest = '';
my $data = {data => [0..999]};
my $fest_file = 'test.xml.js';
my $tt_file = 'test.tpl';

my $v8 = V8::MonoContext->new or die;
my $tt = Template->new(
        ENCODING        => 'UTF-8',
        COMPILE_DIR => '.',
        COMPILE_EXT     => '.ttc',
) or die;

my $t0 = [gettimeofday];
foreach (0..999) {
        my $out = '';
        $tt->process($tt_file, $data, \$out);
        $result_tt .= $out;
}
printf "TT: %f, checksum: %s\n", tv_interval($t0), md5_hex $result_tt;

$t0 = [gettimeofday];
foreach (0..999) {
        my $out = '';
        $v8->ExecuteFile($fest_file, \$out, {json => encode_json($data), append => ';fest["test.xml"]( JSON.parse(__dataFetch()) );'});
        $result_fest .= $out;
}
printf "FEST: %fsec, checksum: %s\n", tv_interval($t0), md5_hex $result_fest;


Результат
TT: 7.066557sec, checksum: 5df3b88b7b9769de8a30398cf847cb93
FEST: 0.357796sec, checksum: 5df3b88b7b9769de8a30398cf847cb93
1.1. Кроме старого приложения могут потребоваться еще и данные из базы недельной давности… Согласовывать все эти кэши разного уровня тоже непростая задача.sdf

1.2. gzip линейный в плане потребления ресурсов и поэтому беспокоит мало, к тому же он действительно мало потребляет. Конечно, если у вас сервер раздает статику терабайтами, то и gzip начнет вносить свой вклад в нагрузку. Тогда можно заюзать nginx.org/ru/docs/http/ngx_http_gzip_static_module.html
80КБ — это веб версия, конечно

3. На мой взгляд проблема в том, что нода как асинхронный сервер плохо подходит для задач, требующих значительных ресурсов CPU. То что ее можно поднять в огромном количестве на нескольких серверах вовсе не значит что это оптимальный вариант. Железо у нас тоже не безлимитно.

p.s. Скорость ответа для поисковиков, насколько я помню — это одна из метрик при ранжировании…
У нас был проект, который шаблонизировался полностью на клиенте. Основные проблемы у нас возникали как раз с мобильными пользователями. Конкретное время, которое тратилось на шаблонизацию тогда я сейчас уже, увы, не скажу…
Проблема в том, что у нас разный html генерится для веб и мобильной версии, поэтому сравнивать было бы некорректно.
Это больше синтетический тест. В реальности кроме котиков еще есть вагон js-библиотек, которые используются при рендеринге шаблона. Их тоже надо притащить клиенту, скомпилировать и т.п.
В нашем случае это все живет внутри воркера и грузится в него один раз.
Кроме этого шаблонизация на сервере более предсказуема в плане нагрузки, не говоря уже о мониторинге и прочих метриках за которыми становится легко следить.
Да, нас заботит время первой отрисовки, также как и все последующие )
Тема client-side vs server-side rendering довольно холиварная, но все же отвечу :)

1. Здесь все не так однозначно.
Во-первых, данный показатель сильно зависит от того как часто одни и те же юзеры заходят на проект, сколько делают внутренних переходов, от того как часто проекты релизятся и т.п.
У нас в контент проектах человек может зайти, например, в пятницу на tv.mail.ru, посмотреть главную страницу со своими любимыми каналами и уйти на неделю не сделав ни одного дополнительного хита. В середине следующей недели мы выкатываем релиз, в котором очень вероятно изменился какой-то компонент, из-за чего пришлось пересобирать шаблоны и js-библиотеки. В итоге клиент при заходе на тот же ресурс через неделю опять получает всю пачку статики.

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

Размер json данных и готового html я бы не стал учитывать вообще, т.к. после gzip они будут иметь сопоставимый вес. Например, если взять главную одного из наших проектов, то на ней html весит 300КБ, а данные для нее 80КБ. После gzip — 45 и 17 соответственно. Да, разница есть, но несущественная.

2. Мы держим v8 в продакшене, который работает в контексте привычного для нас языка и используем его только для шаблонизации. С нагрузкой справляется хорошо, иначе бы не было этой статьи :)
Именно для того, чтобы снять лишние вопросы мы решили выложить все свои наработки по этой теме в паблик. По исходникам должно быть видно, что решение очень простое и все во что тут можно упереться — это сам v8, а точнее его gc, но с этим вы столкнетесь и на ноде, только в еще большем объеме.

3. Реализаций сишных «прослоек» великое множество на любых языках. Они пишутся для того чтобы ускорить какие-то части кода, если стандартных средств языка не хватает. Либо если требуется сделать биндинг к какой-то популярной C/C++ библиотеке. Считайте что это как раз наш случай. Сама прослойка очень тонкая и является, по-сути, интерфейсом к v8monoctx.so. Ее реализация на Perl, Python и PHP весьма простая и она не привносит практически никакого оверхэда в работу проекта.
Сам модуль v8monoctx тоже простой. Его основная задача состоит в том, чтобы создать контекст v8, загрузить в него один раз все js-тулзы и дальше заниматься только компиляцией/кэшированием и выполнением самих шаблонов.

А вот в «железности» Вашего решения есть несколько сомнений:
— речь идет не просто о «взять вебкит, загрузить в него страницу и сдампить полученное дерево», а о работе ноды + вебкита + prerender.io. Это заведомо более длинная цепочка технологий, чем просто один голый вебкит. В нашем случае есть только v8.

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

— ну, и, пожалуй, самый главный момент. Node.js — это в первую очередь асинхронный сервер. Поэтому его основная задача состоит в том, чтобы обслуживать кучу соединений, а не исполнять код, который требует значительных ресурсов CPU. Либо надо делать какой-то prefork сервер из нее, что уже точно будет выглядеть как зоопарк.

Как уже было сказано в статье наши шаблоны отрабатывают в среднем за 20мс, плюс к этому могут случаться всплески из-за работы gc.
Поэтому предлагая ноду в качестве сервера надо понимать, что пока идет шаблонизация или gc при обработке какого-либо запроса, все остальные клиенты будут ждать свою очередь! Именно поэтому мы выполняем v8 в синхронном режиме в наших обычных воркерах. Они просто предназначены для задач, в которых активно используется CPU.
Если проводить аналогию, то тут напрашивается nginx. Если начать при помощи него ресайзить картинки, писать кучу бизнес-логики на LUA, вкомпилить в него perl и т.п., то nginx превратится в тыкву и вместо обслуживания тысяч запросов будет ждать когда освободится процессор.

Конечно, можно представить, что запросов от поисковиков сравнительно мало, но ситуация опять не так однозначна… Яндекс и Гугл частенько приходят со своими краулерами одновременно, плюс к этому всегда есть пара ботов, которые маскируются под поисковик.
А поскольку «браузер» на бэкенде требует больше ресурсов для рендеринга, то нагрузка от поисковиков может оказаться не такой уж и маленькой как кажется.
И самое противное в этой истории, что любой сканер, ab, siege и т.п. может в любой момент прикинуться гуглом и устроить нагрузку, которую довольно сложно прогнозировать.

Мне кажется предложенное Вами решение можно применять в несильно нагруженных проектах где уже используется нода.
Либо в проектах типа Google Analytics, где вообще ничего не надо индексировать и сайт работает как одно большое приложение написанное на js. Там бэкенду достаточно отдавать только json, а вся шаблонизация ложится на плечи клиента
1. Скорость визуализации html-я в браузере полученного напрямую с сервера все равно будет быстрее чем загрузка страницы, которая должна будет потом получить данные для рендеринга, затем сам шаблон и только потом сделать шаблонизацию клиентом.

2. Дополнительно придется держать ноду в продакшене на всех бэкендах с дополнительной библиотекой, которая не очень понятно как справляется с нагрузкой.

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

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity