Хабрахабр

Королев. Лекарство для веба

Судя по положительным откликам, разработчикам небезразлично качество производимых продуктов. Около года назад вышла статья-манифест Никиты Прокопова о разочаровании в программном обеспечении. Может быть пора начать действовать?

Проблемы такие: большой вес JS-кода, высокое время до начала работы со страницей (TTA), высокое потребление памяти и процессора. В этой заметке я хочу рассказать о своей разработке, которая, по моему мнению, может вылечить основные проблемы производительности современного веба и сделать пользователя немного счастливее.

Попробуйте поиграть пару партий. Прежде чем читать дальше, перейдите по ссылке. Играть желательно с десктопа.

Немного истории

Браузер отображал гипертекстовые страницы, которые получал от сервера. С самого начала веб-браузер был задуман как тонкий клиент для веб-серверов. Как это часто бывает, красивая идея столкнулась с реальностью, и уже через несколько лет производители браузеров добавили поддержку скриптового языка. Просто и изящно. Сначала он служил лишь для украшательства и вплоть до середины нулевых хорошим тоном считалось делать веб-страницы работающими без поддержки JS.

Задачи по улучшению интерактивности легли на плечи верстальщиков. Современный подход к разработке веб-сайтов это результат развития требований к интерактивности пользовательского интерфейса. Верстальщики научились писать JS и превратились во фронтендеров. Те зачастую не имели компетенций и полномочий разрабатывать "сквозное" решение. Фронтендеру удобно писать для браузера, а бекендкеру удобно не думать о пользователе (мое дело тебе JSON отдать, а дальше хоть трава не расти). Логика постепенно начала перетекать с сервера на клиент. Буквально два года назад можно было наблюдать всплеск интереса к serverless-архитектуре, где предлагалось что JS-приложения будут напрямую работать с БД и шинами событий.

Основная логика выполняется на толстом клиенте, серверная часть вырождается до простой прослойки к БД. На данный момент "сферический веб-сайт в вакууме" представляет собой сложное приложение на JS и простой API-сервер с которым оно общается.

Если сервис "взлетел" и стал зарабатывать, то дальше, в плане производительности, ему будет только хуже. Необходимость держать логику на клиенте порождает проблемы. Будет меняться команда разработки. Будут меняться требования. Страница распухнет от зависимостей, будет загружать JSON о назначении которого уже никто не помнит, но удалять страшно, вдруг что-то сломается. Нового кода будет все больше, а старый код не будет вычищаться. Закончится это тем, что выгоревшие фронтендеры придут к менеджеру с предложением переписать все с нуля на новом фреймворке. Будут появляться фоновые задачи setInterval, каждая из которых выполняется по несколько миллисекунд каждую секунду, что через какое-то время приведет к тормозам и разогреву айпада несчастного пользователя до того, что он сможет жарить на нем яичницу. Менеджер откажет, и фронтендеры станут использовать два фреймворка совместно.

Как работает Королев

Тому моменту, когда кому-то пришла в голову идея обновлять контент без перезагрузки страницы, и историческая неизбежность породила AJAX? Так вот, что если вернуться к точке отчета? Лучшие сайты делают пререндеринг страниц на сервере (SSR), чтобы пользователь видел интерфейс до того как загрузится и запустится JS. Что если оставить все на сервере и сделать тонкий клиент? Мысли об этом вылились в проект "Королев". Можно пойти дальше и оставить на клиенте только код, который отвечает за обработку ввода-вывода учитывая современные требования к интерактивности.

Пользователь приходит на страницу. Как это работает со стороны клиента? Когда пользователь производит событие (например клик), скрипт отправляет его на сервер. Сервер отдает сформированный HTML и небольшой скрипт (~6 Кбайт без сжатия), который соединяется с сервером по веб-сокету. Клиент применяет список команд к DOM. Обработав событие, сервер формирует список команд вида "добавь новый <div> туда-то", "добавь класс к такому-то элементу", "удали такой-то элемент". Как таковой работы с HTML не происходит — скрипт работает непосредственно с DOM, поэтому не стоит беспокоиться что содержимое формы или позиция скролла будет сброшена.

Когда от браузера приходит запрос на страницу, Королев создает новую сессию. Что происходит на сервере? Из этого состояния формируется HTML, который отдается клиенту в качестве ответа на запрос. Для сессии производится начальное состояние, которое сохраняется в кеше. После запроса страницы, сервер принимает запрос на открытие веб-сокета. Кроме этого сервер сохраняет в сессии "виртуальный DOM". Каждое событие приходящее с клиента может изменить состояние связанное с сессией. Королев связывает открытый веб-сокет с сессией. В результате сравнения получается список команд для отправки на клиент. Каждое изменение состояния приводит к вызову функции render, которая формирует новый "виртуальный DOM", который сравнивается со старой версией.

Описанное выше могло напомнить вам React, с тем отличием что все выполняется на сервере. Что происходит в коде и голове разработчика? Поэтому, если вы работали с React или другим "виртуальным DOM", то подход Королева будет вам знаком. С точки зрения подхода к разработке мы так-же имеем что-то подобное. Представьте, что по шаблону раскиданы обработчики событий которые умеют изменять данные (но не DOM). Если вы не знакомы с React, то представьте что у вас есть данные которые вы вставляете в шаблон. Королев сам придумывает как изменить DOM. Вы изменяете данные, страница изменяется сама.

Производительность

Оба вопроса весьма резонные. Есть два популярных вопроса по поводу Королева: "что если задержки высокие" и "не нагрузит ли это мой сервер". Это значит что изменения произведенные ей применятся как только JS-машина закончит выполнение кода и браузер начнет рендеренг. Фронтенд-программист привык что его программа работает на локальной машине пользователя. Вы могли наблюдать задержку только если заходили с другой стороны земного шара (сервера расположены в Москве) или сидели в интернете через GPRS. Я специально показал пример использования "на максималках" в самом начале. Ну или мой жалкий виртуальный сервак с одним ядром и 1 Гбайт оперативки не выдержал хабра-эффекта.

Движок вывода изменений работает очень быстро: ~10 тыс. Вопрос про нагрузку на сервер задают обычно бекендеры. Статический рендеринг тоже дает довольно хороший результат: до 1 млн. выводов в секунду для двух произвольных деревьев из 500 узлов на младшем макбуке 2013 года. Каждый "виртуальный DOM" хранится и обрабатывается в специальном сереализованном виде и занимает 128 Кбайт для средней веб-страницы. страниц в секунду. Процесс вывода специально оптимизирован и не имеет оверхеда по памяти и GC.

Не нужно писать дополнительную прослойку между БД и сервером. Что касается скорости разработки, то здесь Королев дает большие преимущества. Не нужно заботиться о модульности фронтенда — вес JS на клиенте всегда останется одинаковым. Не нужно согласовывать протокол между клиентом и сервером. Не нужно делать дополнительную логику для серверных событий: просто примите сообщение из очереди и измените состояние сессии, Королев отрендерит и доставит.

Цена

От каких-то привычек придется отказаться, а какие-то новые приобрести. За преимущества придется заплатить. Придется научиться делать инфраструктуру изначально геораспределенной, если хотите обслуживать пользователей из разных стран качественно. На пример придется отказаться от JS-анимаций и удовлетвориться CSS-анимациями. Придется отказаться от JS и перейти на Scala.

Дочитали бы вы до этого момента, если бы я сразу рассказал об этом? Мне немного стыдно (на самом деле нет), что я ввел читателя в заблуждение и не сказал сразу что Королев написан на Scala. Первый связан с тем что серверный рендеринг воспринимается как что-то медленное, не интерактивное. Рассказывая о Королеве мне приходится преодолевать два стереотипа. И первый и второй стереотип не имеют к реальности никакого отношения. Второй связан с тем что Scala это что-то сложное. Современный JS тяготеет к функциональному программированию, Scala дает его из коробки. Более того программировать в стиле React на Scala удобнее чем на JS. Иммутабельные коллекции встроены в стандартную библотеку Scala. На пример, у любого объекта в Scala есть метод copy(), который позволяет скопировать объект изменив некоторые поля.

Заключение

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

Ссылка на проект на гитхабе

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть