Что происходит, если вдруг на клиенте пропадает интернет? Возможно, на долю секунды, а может быть, на более ощутимый период? Все мы как пользователи сталкиваемся с нестабильным сигналом, плавающим качеством связи. Иногда это неважно, ведь хочется посмотреть какое-то весёлое видео, иногда от этого может зависеть очень многое – представьте, что вам срочно надо купить билет на поезд или оплатить тот же самый интернет.
Те сферы, где очень важен конечный пользователь – например, СМИ, говорят, что уже 13% пользователей уходят, если ваш сайт открывается больше четырёх секунд, не разбираясь в причинах. А теперь давайте представим такого пользователя, который еще пробует отправить комментарий, и он постоянно «отваливается» из-за проблем со связью?
Процент уходов и отказов будет заведомо больше. Как этого избежать? Что можно сделать в ситуации, когда данные должны быть гарантированно отправлены как от клиента, так и со стороны сервера?
На этот и другие вопросы отвечает Андрей Ситник – автор PostCSS и Автопрефиксера, ведущий фронтендер в «Злых Марсианах».
– Почему мы вообще говорим о проблемах связи? Разве это не вопрос, исключительно связанный с физическими/сетевыми возможностями?
– OSI тут не совсем в тему. Logux заменяет REST и AJAX. То есть это чисто прикладной уровень. Logux решает следующие проблемы:
- Сейчас требуется много кода для простых запросов.
- Живое обновление данных с сервера писать на порядок сложнее.
- Хорошую поддержку оффлайн вообще ад написать. Но оффлайн нужен всегда, так как у нас всегда есть «500 мс оффлайн» — эти постоянные крутилки на кнопках.
Для этого Logux как бы создаёт особый виртуальный канал событий между клиентом и сервером.
Клиент может положить в этот канал события — Logux сам их отправит, когда можно будет. И то же самое на сервер. Давай разберём это на техническом уровне пошагово:
Разбор полетов
1. На клиенте это будет библиотека с API один-в-один как у Redux (к Реакт не привязана, есть и более низкоуровневый API). Ты точно так же создаёшь action. Но у некоторых action можешь выставить ключ sync: true — тогда Logux доставит их сам на сервер. Эта библиотека держит веб-сокет, посылает пинг, проверяя, что связь есть.
– Так, а чем хорошо то, что эта библиотека не привязана к Реакту?
Не все разрабатывают на Реакте (и это очень хорошо для разнообразия среды). Кто-то может использовать Vue.js, Angular. Или просто иметь JS-приложение, где нет HTML, так что Реакт будет не так нужен. Идём дальше:
2. Дополнительная библиотека, которая при потере связи покажет спец. бейджик «Нет связи» вверху страницы и изменит фавикон. Если в канале на отправку окажутся события, а связи не будет — она тоже скажет пользователю, что не все данные сохранились.
– Зачем пользователю это знать? Для какого формата взаимодействия это может быть критически важным?
– Если пользователь передал нам какие-то данные, то мы обязаны его уведомить, что они не сохранились. Я бы сказал, что это поведение должно быть по умолчанию. Библиотека опциональна во многом, чтобы написать самому такое уведомление и лучше встроить его в дизайн. Хотя есть случаи, когда можно не показывать проблемы со связью — например, если Logux используется для сбора действий пользователя для аналитики.
Следующий шаг.
3. Когда связь появится, Logux-клиент и сервер отправят друг другу те события, которые добавились за время отсутствия связи.
– Понятно, логично, сколько действий может быть в очереди?
– Обычно событий 10-20. Но у нас нет жёстких ограничений — сколько поместится в памяти клиента и сервера.
– Порядок выполнения этих событий?
– Порядок событий контролируется очень строго. Тут же проблема не только в порядке событий одного пользователя. Но и при работе нескольких пользователей — порядок всех их событий должен быть одинаковым на всех системах (чтобы итоговое состояние было одинаковым). Поэтому в Logux каждому событию присваивается время создания. Оно довольно хитрое — например, учитывается и разница времени между клиентом и сервера.
– Не будет ли так, что какие-то события уже не надо выполнять? Взаимная проверка зависимости действий?
– Клиент может чистить лог от уже ненужных событий. Это уже определяется бизнес-логикой.
Следующий шаг: пользователь может закрыть браузер — неотправленные события сохранятся в localStorage. Но перед попыткой закрытия библиотека попросит пользователя сначала выйти в интернет, так как есть неотправленные данные.
– Опять возвращаемся к вопросу ёмкости и важности, и что произойдёт, если пользователь принудительно закроет браузер?
– Ничего страшного. Как только пользователь снова вернётся на сайт — данные сами отправятся.
Теперь все оставшиеся шаги:
5. Для написания Logux-сервера есть специальный серверный фреймворк. То есть это как express.js. Ты сам описываешь, как сервер реагирует на то или иное событие с клиента. При этом логика чуть хитрее — то, что событие пришло сейчас, не значит, что оно было создано сейчас. Поэтому у каждого события есть время создания. При соединении клиент и сервер определяют разницу времени и синхронизируют часы (и время создания старых событий).
6. Фреймворк для сервера пока на node.js. Потом сделаем для Elixir и Go. Но можно использовать и Ruby, Python и PHP — просто поставить node.js сервер как прокси, чтобы держать сокет. А уже этот прокси-сервер будет посылать старый REST-запрос в ruby-сервер.
7. Поскольку у нас есть этот лог событий и точное время, поверх него можно сделать CRDT как в Swarm.js — и автоматически разрешать конфликты во многих простых случаях.
8. Вообще, в большинстве случаев можно не рисовать крутилку — клиент может сразу отрисовывать, будто событие уже выполнилось (например, комментарий отправлен), Logux сам покажет пользователю, что данные не сохранились на сервер. Само собой, будут случаи, где так не получится — например, оплата. Там можно делать старую логику с крутилкой.
Logux и альтернативы
– Спасибо, а чем Logux тогда лучше уже существующих решений?
– Есть Relay/GraphQL — они сокращают количество кода при запросе данных. Но изменение самих данных тут тоже реализовано не очень просто. Живое обновление решено плохо. Оффлайн тоже не проработан.
Есть изоморфные базы данных — они работают на клиенте и на сервере и синхронизируют данные между собой. Например, CouchDB и Firebase. Там хорошо решено живое обновление данных. Готовый CRDT сейчас мало у кого есть, но, в целом, реализовать его можно. Но их довольно сложно расширять, многие разработчики боятся такого странного подхода к синхронизации, поэтому такие базы данных и не стали индустриальным стандартом.
Есть ещё CRDT-библиотеки — например, Swarm.js. Logux копирует много идей у Swarm.js. Но Swarm.js сложно подружить с Реактом и Редаксом. Также сложно передавать в нём не-CRDT операции.
– В чём сильные и слабые стороны?
– Меньше кода, живое обновление данных из коробки, легко сделать приложение для работы в оффлайн. И всё это со знакомой семантикой Редакса.
Главная слабая сторона — надо запускать дополнительный сервер. Но это есть у всех таких решений. Плюс этот сервер можно сделать прокси-сервером и всю логику продолжать хранить в PHP или Ruby on Rails.
– Откуда вообще появилась потребность в этом выделенном решении?
– На Амплифере нужно было показывать живую статистику публикаций и обновлять страницу с ошибками сразу же при появлении проблемы. И мы поняли, что хорошего решения не нашлось. Попробовали внедрить Swarm.js, но легко развернуть его не получилось. Так что я начал думать, как подружить Swarm.js и Редакс. В Питере в это время как раз был Дэн Абрамов, и в разговоре с ним родилась идея отдельной библиотеки.
– Имеет ли это шансы на то, чтобы стать индустриальным стандартом?
– Я стараюсь сделать Logux, как универсальное решение для всех веб-приложений. Но, мне кажется, в 2017 мы увидим много ещё попыток переделать AJAX/REST — сейчас это главная проблема веб-разработки, на мой взгляд. Кто победит в этой борьбе, узнаем только в 2018.
Примеры из жизни
– Есть ли 1-2 интересных практических кейса?
– Да, даже TODO MVC — вам же хочется продолжить работать со списком задач даже, если связи нет. Или если вы добавили задание на компьютере, то хотите, чтобы оно появилось тут же и на телефоне, без перезагрузки страниц.
Или комментарии — живое обновление, как в Фейсбуке или ВК, полезно для вовлечения людей.
Есть ещё одно преимущество Logux для любого сайта — это «оптимистичный интерфейс» (о нём как раз рассказывал другой спикер HolyJS). Сейчас при каждом действии мы показываем пользователю «крутилку». Каждое сохранение блокирует интерфейс и нарушает поток пользователя. С помощью Logux на порядок проще делать оптимистичный интерфейс, где сохранение будет происходить в фоне, отвлекая пользователя, только если ошибка действительно произошла. Например, так работает Google Inbox. Любое веб-приложение выиграет от оптимистичного интерфейса, так как пользователи всегда любят быстрые интерфейсы.
– Большое спасибо за интервью, и до встречи на конференции.
Конечно, в интервью удалось обсудить только общий взгляд на библиотеку и ее философию, на HolyJS Андрей выступит с подробным часовым докладом о разработке и работе с Logux. Кроме того, можно будет сразу посмотреть доклады, посвященные и другим обсуждаемым технологиям:
Автор: JUG.ru Group