---
title: Datasync между браузерами
date: 2016-06-02T10:00
tags: [frontend]
---

При постройке качественного single-page приложения рано или поздно встаёт вопрос **синхронизации данных**. Это тем более актуально, когда дело касается работы разных пользователей. Приложения типа google wave, google docs, push-notifications в IOS, подняли планку ожидания пользователей и от обычных веб-систем

### Что даёт синхронизация

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

Улучшает **безопасность** — если пользователь выходит из одного таба, его можно выкинуть из остальных автоматически

Снижает нагрузку на сервер — можно высылать оповещение о состоянии файла, если он обрабатывается в реальном времени, подобно ютубу без какого-либо взаимодействия с пользователем или поллинга

### Сложности реализации

Если вы решили что это must-have фича, прежде чем начать реализовывать её у себя, задумайтесь о последствиях:

- Вам понадобится транспортный механизм (см. ниже), который обычно ограничен количеством подключённых пользователей (макс. 10 тыс), размером сообщения и объёмом траффика
- Безопасность. Транспортный механизм и источник оповещения должен точечно высылать данные **только нужному пользователю** (без broadcast-а).  
    Лучше всего источник оповещения ставить на backend, сразу после закрытия транзакции. В этом случае полезно привязываться к сессии на бэкэнде
- Я советую не использовать datasync-сервис в качестве источника данных. Это должен быть легковесный вестник адреса по которому можно прочитать детали. Более того, формат этого сообщения должен быть стандартизирован. Например `{action:'file.added', id:1345}`
- Весь front-end код должен хранить данные в сыром виде в сервисах/моделях, которые будут точечно обновляться.  
    Соответсвенно все события будут делится на прямое вмешательство пользователя и на datasync-действия  
    Код при этом будет запускать похожие функции над данными
- Все use-кейсы усложняются из-за ситуаций 
    - что делать если A удалил данные который B просматривает (редиректить B, блокировать A, показывать оповещение B?)
    - что делать если A и B одновременно сохраняют изменения над одним и тем же обьектом (провести обе операции, кто последний тот и победил? провести первую и заблокировать последующие до окончания операции?)
    - что делать если A составляет новый обьект 3, ссылающийся на обьект 2, который удаляет B, а потом происходит сохранение (удалять include при удалении или при сохранении?)
- Если вы используете систему привилегий, то надо дополнительно иметь оповещения о присвоении или изъятия обьекта из видимости пользователей
- Если ваш сервис синхронизации вдруг упадёт, то при поднимании надо сделать так что-бы сообщения не потерялись из-за ещё неподключившихся пользователей (у socket.io 5 сек)

### Polling

Не ушёл в небытие, я по-прежнему вижу как некоторые сайты настойчиво стучат в бэкэнд. Например движение общественного [транспорта в Таллинне](http://soiduplaan.tallinn.ee/#bus/3/a-b/11308-1/3/map). Видимо это проще всего реализовать и проще всего использовать вместо API. Что может быть проще для сторонних разработчиков - просто читай .json файл так часто как тебе это надо. А он в свою очередь может выплёвываться из кэша-в-памяти

Pusher  

[Пушер](http://pusher.com/) — сервис который вобщем-то следующий по сложности для разработчиков. Внедряете скрипт, подписываетесь на события в js. Высылаете события, которые будут получать все пользователи.

Лучше всего это делать на уровне backend'а, иначе захламите frontend и могут возникнуть редкие баги. Например — поставил заливаться файл и закрыл браузер, а клиент X не получил итогового оповещения. Для более тонкой настройки прийдётся повозиться с интеграцией авторизации и токенами

<iframe width="408" height="260" src="https://www.youtube.com/embed/rk5Jm1IHxlI" title="You Have Real-Time Data. You Just Don&#39;t Know It!" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe width="408" height="260" src="https://www.youtube.com/embed/Xip2TgAEVz4" title="Real-Time Web Apps in 2015 &amp; Beyond" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
### Firebase  

Наконец, ещё один сервис, купленный гуглом, который больше позиционируется как nosql-база данных с синхронизацией между клиентами. Для того что-бы настроить её, как систему оповещений, но при этом оставить хранение данных у себя, прийдётся повозиться. Изначальный setup такой же простой как у pusher'а, да и авторизация по токену такая же, но поскольку это БД, то подписываться надо не на **события оповещений**, а на **изменения json-схемы**. Саму json-схему вы делаете сами, но я советую для этого случая использовать что-то типа..

```json
{
    notifications:[
        2345:[
         'file X added by 2346',
         'file Y deleted by 2346'
        ],
        2346:[
         'file X added by 2345'
        ]
    ]
}
```

<iframe width="816" height="350" src="https://www.youtube.com/embed/wf9hZcqQI7A" title="Google I/O 2015 - Developing Extraordinary Apps with Firebase" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

### Websocket-сервер / Socket.io

Socket.io вы должны хостить сами и для этого нужен nodejs. Socket.io реализует браузерные веб-сокеты, протокол которых менялся несколько раз. Из-за этого могут быть трудности с подключением со стороны php к нему. Я использую [elephant.io](https://github.com/Wisembly/elephant.io), пока полёт нормальный. Самый простой начальный вариант —  socket.io принимает подключение и делает broadcast всем подключённым клиентам. Для более тонкой настройки прийдётся повозиться с куки, php сессиями и желательно [переносом их хранения в БД](http://culttt.com/2013/02/04/how-to-save-php-sessions-to-a-database/), что-бы backend мог высылать безопасно сообщение конкретному подключённому пользователю, после чего контролировать по session_id + socket кто что будет получать.

У socket.io есть альтернативные библиотечки — [socky](https://github.com/socky) (любит руби), [sockJs](https://github.com/sockjs), вроде как очень быстрый [ws](https://github.com/websockets/ws), [engine.io](https://github.com/socketio/engine.io), [data.io](http://scttnlsn.github.io/data.io/). [Faye](http://faye.jcoglan.com/) я сам не пробовал, но судя по [описанию](https://docs.cometd.org/current/reference/) растёт ещё со времени когда терминология с _кометами_ была на слуху. Видимо теперь тоже работает на веб-сокетах


[img/85.pdf](img/85.pdf)

## Related

- [AddHandler в Visual Basic 2005](/ru/blog/tech/frontend/addhandler-v-visual-basic-2005/)
- [Drag-n-drop file upload](/ru/blog/tech/frontend/drag-n-drop-file-upload/)
- [Post form с window.open](/ru/blog/tech/frontend/post-form-s-windowopen/)
