- PVSM.RU - https://www.pvsm.ru -
Написал недавно движок [1] для синхронизации данных, имеющий первоклассную поддержку оффлайна. Например, можно уйти в оффлайн, изменять данные, закрыть браузер, открыть браузер, открыть сайт (выйти в онлайн) и данные смержатся без потерь. Также во время онлайна данные между клиентом и сервером синхронизируются в реальном времени. Хочу рассказать, в чём была идея, какие есть подобные решения/технологии и кому это может пригодиться.
Требования к веб-приложениям всё время повышаются. Если раньше все были довольны статическими страничками, то сейчас пользователи хотят, чтобы комментарии под картинками котиков обновлялись сразу, лайки накручивались на глазах, уведомления о событиях приходили не дожидаясь перезагрузки страницы. С ростом популярности мобильных устройств, появилась концепция Offline First [2], с идеей, что приложение должно учитывать нестабильность или отсутствие сети.
Веб — это распределенная среда. А синхронизация данных в распределенной асинхронной среде — задача не простая. Всё облегчается тем, что для большинства приложений нету строгих требований к достоверности отображаемых данных. Не так важно, если с сервера не дойдёт какой-то комментарий для картинки с котиком или если два пользователя увидят чуть разное кол-во лайков.
Обычно, в таких случаях, к уже существующему REST-api прикручивается реализация реал-тайма, где сервер каким-то образом знает, какие данные интересуют клиента в данный момент времени (подписки) и при изменении этих данных в бд, шлёт клиенту патчи с обновлениями через web-socket соединение, либо используется какое-то готовое решение. Давайте посмотрм поближе, что сейчас популярно использовать для работы с данными в вебе.
Flux [3] — методология (и реализация) для работы с данными от Facebook. Главная идея — однонаправленность. Как именно данные достаются с сервера не входит в область интереса Flux. При желании, можно сделать Flux-сторы реалтаймовыми.
Redux [4] — новая и самая популярная Flux-подобная библиотека. Отличается от Flux простотой (нет диспатчера, один стор итп).
Relay [5] — новый фреймворк от Facebook, пришедший на смену методологии Flux. Каждый React компонент, может запросить с сервера данные, которые нужны ему. Делается это с помощью GraphQL [6] языка. Такой абстрактный язык запросов может быть хорошим решением, если у вас много разных источников данных (баз данных), но при этом появляется необходимость руками описывать, как он преобразуется в языки запросов баз данных для всей схемы данных. Relay позволяет запросить только часть документа и разрешает ситуации, когда несколько компонентов запросили одни и те же данные, что полезно, когда у вас большое кол-во компонентов. Подписки должны скоро появиться [7].
Falcor [8] — движок от Netflix. Имеет единый интерфейс для работы с локальными и удалёнными данными. Также интересна концепция paths.
Meteor [9] был своего рода революционером и сильно продвинул идеи изоморфного api для работы с данными и реалтайма. Подписка осуществляется напрямую на монго-запросы, а в качестве патчей при обмене данными выступают операции из монговского oplog'а. Meteor — это даже не фреймворк, а скорее платформа, со своим пакетным менеджером.
Firebase [10] — реалтаймовый BAAS. Интересный и довольно популярный платный сервис, решающий проблему реалтайма в веб-приложениях.
Diffsync [11] использует алгоритм нахождения diff'ов для JSON-объектов, похожий на то, что делает Git для строк. Затем, клиент и сервер обмениваются этими diff'ами. Это может не плохо работать, если у вас в приложении не высокая коллаборативность.
Для того чтобы полностью застраховать себя от недостоверности и от потери данных не достаточно обмениваться патчами, нужен более серьезный подход. Есть две техники разрешения конфликтов для распределенных асинхронных систем — OT [12] и CRDT [13]. Данные представляются в виде состояния и лога операций. Состояние является результатом последовательного применения всех операций из лога и имеет свою версию. Обычно минимальной сущностью состояния является документ и в качестве хранилища используются документо-ориентированные базы данных. Лог операций может храниться в том же или другом хранилище. Помимо этого, вместе с состоянием хранятся определенные метаданные — версия состояния, таймстемп, тип данных и тп. У CRDT есть еще state-based вариант, который используется, например, в Riak [14]. Но передавать, при каждом изменении, весь документ (состояние) не так эффективно, как только одну операцию, по этому обычно в вебе используются op-based CRDT.
ShareJS [15] — самая популярная реализация OT. Я уже писал [16] про нее. Можно добавить, что главными достоинствами ShareJS являются операции над строками, массивами, числами (это полезно, если, например, делаете коллаборационный редактор), а также наличие реализации общего JSON типа данных и возможность подписки на Mongo запросы. Для OT нужен источник правды, где собственно и преобразуются операции. Обычно, это сервер. Реализовать полноценный оффлайн для OT — задача крайне сложная (не знаю ни одной реализации). Надо сказать, что OT активно используется Google в сервисах типа Docs, Wave. Мы, в компании [17], где я работаю, используем ShareJS (как часть фреймворка DerbyJS [18]) уже ни один год и полёт нормальный.
Все описанные выше движки/библиотеки не имеют полноценной поддержки оффлайн, потому что для этого нужны равноправные распределенные реплики данных, как в Git, глобальные версии состояний и тп. Тут есть разные подходы, но наиболее интересным, на мой взгляд, является CRDT. Решения в этой области выглядят так:
Hoodie [19] — Offline First фреймворк, завязанный на CouchDB. В новой версии на клиенте будет использовать PouchDB [20]. CouchDB хранит всю историю состояний документов, это и используется для оффлайна. Можно провести аналогию с Git — при оффлайне, история состояний разделяется на две ветки: серверную и клиентскую, а при онлайне они мержатся. Чем-то похоже на state-based CRDT. CouchDB — по большому счёту key-value хранилище, есть так же базовая реализация запросов, но не такая богатая, как, например, в Mongo.
Swarm [21] — CRDT движок, разработанный нашими сибирскими [22] учёными [23]. Имеет много типов данных — key-value, строки, массивы и тп. Swarm не завязан ни на какую базу данных и, соответственно, не поддерживает подписку за запросы. Реализвация подписок на запросы в общем виде (без завязки на конкретную базу данных) дело не тривиальное.
У Виктора очень интересные [24], доклады [25] и интервью [26].
Общий момент для всех решений является то, что если есть серверная часть, то она написана на Javascript и требует NodeJS. Это объясняется тем, что существенная часть кода между клиентом и сервером переиспользуется.
Наверняка есть еще интересные решения, про которые я не знаю или не вспомнил. Делитесь в комментариях, будем обсуждать.
Идея заключалась в том, чтобы совместить CRDT оффлайн возможности и подписки на Mongo-запросы. Обернуть это в изоморфный Racer [27]-подобный api и интегрировать с React. Добавить наработки из ShareJS по контролю доступа, масштабированию и тп.
По сравнению с SwarmJS пришлось пожертвовать разнообразием типов данных. В отличие от ShareJS и его общего JSON типа данных (включающиего операции над объектами, массивами, строками и числами), в Amelisa каждый документ — это обычный key-value (операции над объектами).
Из Transmit [28] взята идея как реализовать серверный рендеринг для дерева компонентов, каждый из которых подписывается на данные изолированно. Сложность тут в том, что не известны входящие данные для нижележащих компонентов, до момента пока вышележащие не получат данные из базы и не отрендерятся.
Также есть подобие join'ов и возможность смешивать подписки на Mongo-запросы с фетчем данных с обычных url (REST-api, сторонние сервисы и тп.).
Более подробно о возможностях читайте в документации [1]. Хотя в данный момент она оставляет желать лучшего и, возможно, проще посмотреть на пример CRUD-приложения [29], в котором есть авторизация на основе модуля Amelisa Auth [30] и базовый access control. Для самых смелых — исходники [31].
Скорее всего Amelisa будет интересна в тех проектах, где нужен оффлайн и синхронизация данных, но при этом нету требования делать коллаборационные редакторы и тп. Хорошим use-кэйсом может быть todo-list приложение, которые работает на телефоне в виде нативного или веб-приложения и на десктопе в виде веб-приложения. При этом пользователь не хочет думать в онлайне он или в оффлайне, чтобы посмотреть свой список задач на сегодня, отметить законченные или добавить новые. А в момент, когда он окажется в онлайне, данные синхронизируются между всеми устройствами.
Также в требования для Amelisa можно включить React, Mongo, NodeJS.
В данный момент работаю над стабилизацией и интеграцией c React Native [32]. Также мы пишем мобильное приложение, где используется Amelisa и совсем скоро оно должно пойти в продакшен. Лучший способ следить за новостями — подписаться на Twitter Amelisa [33].
Автор: vmakhaev
Источник [34]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/112892
Ссылки в тексте:
[1] движок: http://amelisajs.com
[2] Offline First: http://offlinefirst.org/
[3] Flux: https://facebook.github.io/flux
[4] Redux: https://github.com/reactjs/redux
[5] Relay: https://facebook.github.io/relay
[6] GraphQL: https://facebook.github.io/graphql
[7] появиться: https://github.com/facebook/relay/issues/541
[8] Falcor: http://netflix.github.io/falcor
[9] Meteor: https://www.meteor.com/
[10] Firebase: https://www.firebase.com
[11] Diffsync: https://github.com/janmonschke/diffsync
[12] OT: https://en.wikipedia.org/wiki/Operational_transformation
[13] CRDT: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type
[14] Riak: http://docs.basho.com/riak/latest
[15] ShareJS: https://github.com/share/ShareJS
[16] писал: https://habrahabr.ru/post/206324
[17] компании: http://decisionmapper.com
[18] DerbyJS: http://derbyjs.com
[19] Hoodie: http://hood.ie
[20] PouchDB: http://pouchdb.com
[21] Swarm: https://github.com/gritzko/swarm
[22] сибирскими: https://twitter.com/gritzko
[23] учёными: https://github.com/abalandin
[24] интересные: https://www.youtube.com/watch?v=BfzjuhX4wJ0&feature=youtu.be&t=6h31m10s
[25] доклады: https://www.youtube.com/watch?v=uyZKWyciSXY
[26] интервью: https://radiojs.ru/2015/11/radiojs-33
[27] Racer: https://github.com/derbyjs/racer
[28] Transmit: https://github.com/RickWong/react-transmit
[29] пример CRUD-приложения: https://github.com/amelisa/amelisa-crud-example
[30] Amelisa Auth: https://github.com/amelisa/amelisa-auth
[31] исходники: https://github.com/amelisa/amelisa
[32] React Native: https://facebook.github.io/react-native
[33] Twitter Amelisa: https://twitter.com/amelisajs
[34] Источник: https://habrahabr.ru/post/277645/
Нажмите здесь для печати.