Работа веб-проекта в условиях нестабильного подключения

    Здравствуйте, меня зовут Александр Зеленин, я веб разработчик. Сегодня хочу рассказать, как могут работать сайты в условиях нестабильного подключения как и обещал. Проще говоря, что делать если отключили интернет, а деньги зарабатывать надо.

    Рассмотрим 3 примера: интернет-магазин, кинотеатр и онлайн-плеер. Для магазина и кинотеатра так же идет разделение на 2 части — что делать на стороне пользователя и на стороне оператора/продавца.


    Я не буду рассуждать из-за чего происходят проблемы с подключением и разделять их каким-либо образом. Так же, в рамках данной статьи, потеря подключения рассматривается как редкий случай, а не постоянное состояние. Для подобного функционирования требуется связь с сервером хотя бы раз в день. У каждой задачи есть множество вариантов её решить. Нет смысла писать «тут можно было просто в памяти хранить, незачем писать в LocalStorage», спасибо. Так же обращаю внимание что я специально не писал никакого кода в статье, т.к. в каждом случае требуется индивидуальный подход, да и с течением времени ограничения хранилищ или особенности работы браузеров могут меняться, но суть останется прежней.

    Прошу прощения если некоторые технические решения уже не актуальны — постараюсь описать сам принцип реализации. Если где-то неправ — буду рад комментариям и исправлениям.

    Всё это будет работать в случае OnePageApplication, в иных случаях решения додумывайте сами :-)

    Ситуации:


    Интернет-магазин


    Со стороны пользователя:


    Вы лазаете по страницам, выбираете товары, складываете в корзину. Внезапно пропадает соединение с магазином. Тут же появляется окно со списком уже сложенных товаров и специально сформированный номер телефона, при звонке по которому пользователь попадет на оператора, который сразу же уточнит, куда всё доставлять и желаете ли вы что-то ещё. Особенно применимо к доставке еды. Бывает, интернет отключается, когда уже вводишь номер телефона, жмешь перейти на следующий шаг, и получаешь ошибку подключения. А назад уже не вернуться и номер телефона не посмотреть. Заказ потерян.

    Оператор


    Оператору имеет возможность на отдельной странице посмотреть заказы «в процессе», так же получает сообщения о потерявших связь клиентах. Если пользователь был авторизован, то ему можно перезвонить (прямо в браузере), либо по получению звонка с добавочным номером АТСка сразу выводит данные о заказе. Все довольны.

    Кинотеатр


    Возможность онлайн заказа билета + интерфейс оператора работающий через браузер (может быть и отдельная программа, сути это не меняет) с моментальной отправкой данных о купленных билетах на сервер.

    Со стороны кассира:


    Вырубился интернет, билеты надо печатать, да так, чтобы их не купили онлайн. В браузере блокируется определённое количество мест (прогнозируемое количество покупок онлайн), остальные можно выбирать/печатать без проблем. Данные аккумулируются и оправляются, как только соединение появляется. В случае если места заканчиваются — оператор связывается по телефону с ответственным за работу сайта, и последний имеет возможность выделить ещё участок для продажи оффлайн, а кассир в ручную добавить их как «свободные».

    Со стороны клиента при покупке онлайн.


    Клиент видит на сайте сообщение что нет связи с кассой кинотеатра, но всё так же имеет возможность купить ограниченное количество билетов (либо возможность забронировать билеты по телефону, позвонив прямо из браузера). На билетах печатается специальный уникальный QR код, который проверяется на соответствие при входе в зал.

    Онлайн-плеер


    Сейчас многие отказываются от коллекций музыки на жестком диске в пользу онлайн прослушивания. Казалось бы, сплошные плюсы — место на диске не кончается, пополнить коллекцию можно в пару кликов, она всегда есть на всех устройствах.
    Но вот отключается интернет, и ты сидишь без музыки. Не круто.
    Как вам вариант что, например, «любимые» треки всегда доступны? Уже лучше, когда есть, хотя бы, пара сотен треков.

    Реализация


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

    Как определить, что клиент (пользователь, АРМ оператора) отвалились?
    Да точно так же. Конечно нужно дать хотя бы пару секунд на переподключение, прежде чем впадать в панику.

    Онлайн плеер


    Основная проблема это именно хранение треков на стороне клиента. Во-первых, нужно понять, что мы храним и в каком виде. Храним мы, конечно же, плейлисты, треки, и некоторую мета информацию о них.

    В каком виде хранить треки?
    Base64 или любое текстовое представление файла.

    Как воспроизводить из строки?
    Самый простой способ это просто генерация audio элемента с указанием в качестве источника “data:audio/wav;base64, …”. Есть масса библиотек, найти их не составит труда.

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

    Так, хорошо, и где нам это хранить?
    Вот тут уже сложнее. В зависимости от ситуации (браузера, устройства и т.п.) нам нужно использовать разные хранилища.
    IndexedDB: firefox и chrome на мобильных устройствах, chrome, firefox, IE на десктопах
    WebSQL: android browser, safari, ios web view на мобильных, safari на десктопах
    SessionStorage для android browser, если 200мб websql недостаточно. Но нужно учитывать, что сохранение идет только на сессию.
    Вообще, для мобильных устройств, лучше, конечно, выпускать приложение.

    Что в итоге то?
    У пользователя отключился интернет, даём доступ на страницу с любимыми треками / плейлистами, играем из хранилища по запросу :-)

    Кинотеатр


    Для кассы


    Задача: работать при отсутствии подключения к основному сервису.

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

    Где хранить?
    В данном случае LocalStorage с 5мб должно хватить более чем.

    Механизм работы
    Потеряно подключение -> ограничиваем места в интерфейсе, помечаем проданные билеты флагом что они не сохранены. Для оператора, в целом, ничего не меняется. При появлении подключения отправляем обновлённые данные, получаем данные о проданных онлайн билетах и сеансах на ближайшие пару днейю

    На сайте (при недоступности кассы)


    Задача: не продавать билеты, которые могут быть проданы на кассе.

    Решение: Заранее помечаем прогнозируемое количество мест как «онлайн покупки», в случае потери соединения продаем только их или предлагаем возможность позвонить кассиру.

    На сайте (при обрыве связи клиента)


    Задача: показать сетку сеансов, цены, телефоны для бронирования.

    Решение: сразу загружаем эти данные в LocalStorage, отображаем при потере подключения.

    Интернет-магазин


    На клиенте


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

    На сервере


    Сохраняем данные о всех положенных товарах с привязкой к добавочному номеру. При звонке АТС передает добавочный номер, и оператору сразу выводится вся информация по заказу.
    В случае если это был авторизованный клиент, и нам известен его номер телефона — можно сразу же совершить звонок.

    Итог


    Решать, стоит ли внедрение таких систем затрат, конечно же вам.
    Главный плюс — пользователю не надо устанавливать никакого дополнительного ПО, надстроек и т.п. Всё сразу же работает.
    С удовольствием отвечу на вопросы и расскажу про более конкретные реализации.
    Метки:
    Поделиться публикацией
    Комментарии 20
    • 0
      Честно говоря ожидал обсуждение, хотя бы небольшое. Тема вообще интересна? Писать ещё подобные статьи?
      • +2
        Тема интересна, но сегодня первый из 4 выходных. Обсуждения могут быть в среду.
        • 0
          Ох, что-то не подумал, да. Спасибо :-)
        • 0
          Тема интересна, но вот вы писали что привели бы конкретные примеры. Хотя уже из описанных ситуаций, многие, думаю вспомнят как сталкивались с подобным. Возможно, более правильно было бы рассмотреть подобную проблему в некоторых приложениях или на каких то сайтах. Вот в мобильной версии ВК, бывает не открывается меню при плохом соединении.
          И кстати, какой технологией лучше это реализовывать. Тот же ВК, ужасно работает при плохом соединении. Зачастую приходится обновлять страницу. И на других сайтах бывало подобное. То что-то не загрузиться и все.
          Но в ВК бывает необходимость просмотра сообщений, в целом все норм сделано в приложении и вопрос можно опустить.
          А как насчет сохранения данных формы через клиент или сервер в случае неудачи. Если вы работали с IPB форумами, то думаю, поймете о чем я.
          • 0
            На самом деле это живые кейсы, которые я реализовывал в прошлом, т.е. вполне конкретные примеры, вот как описал.
            К сожалению магазин и плеер сейчас не существуют, а доступ к внутренностям кинотеатра я дать не могу.
            Про технологию тут зависит от задач. В случае с контактом там хватит только грамотного использования шаблонизации на клиенте + отлов «дисконнектов» + хранения некоторых данных в localStorage. Не уходит за пределы данной статьи.

            Сохранять данные формы в LocalStorage — довольно бесхитростно, и восстанавливать при открытии страницы.
            С IPB форумами, и вообще какими либо форумами работать не приходилось.

            Если задашь более менее конкретный вопрос, отвечу как реализовать это в твоем случае с конкретными технологиями.
          • +2
            Мне Ваша статья понравилась. Интересно. Пишите еще.
            Больше подробностей реализации…
          • +1
            Вполне интересно.Даже познавательно, пишите, конечно и дальше
          • –2
            А в чём собственно проблема? Если разработчик дорос до того что бы делать SPA то на слой общения клиент-сервер накладывается ещё одна абстракция — локальный кэш. И дальше всё уже через него.
            В случае с заказами то варианта два — либо специальный код для корзины(6-8 символов) который можно продиктовать или например отсканировать по QR, либо отправка каждого заказа на сервер и сохранение в базе.

            В случае с треками вообще нет проблем — каналы у многих позволяют спокойно забуферизировать в память несколько треков наперёд и краткосрочной отключки интернета даже не заметят.

            Вообще не понимаю чего ожидал автор и каких обсуждений — это тривиальные и очевидные вещи, благо технологии сейчас позволяют это делать по всякому.
            • +2
              По крайней мере, пусть больше разработчиков помнят, что сданный заказчику продукт при соединении 100 Мбит/с и пинге <10 мс это ещё не всё. Теоретически проблем нет, а реально, не так мало сайтов просто начинают странно себя вести при большом пинге и/или частично пропадающих пакетах. Даже если они не думают: «Все кто не на оптике или витухе — нищеброды, деревенщина и не наш клиент», получается некрасиво. Тем более, слишком много недоросших и до оффлайн-ситуаций.
              (С ужасом вспоминая об одном известном 4g-провайдере).
              • 0
                Ага, а ещё забывают о ребятах, которые банально далеко находятся от Москвы. Типа во Владивосток, Хабаровск. И соединение до серверов в Мск может быть довольно медленным.
              • 0
                Проблем никаких нет как раз :-) Всё решаемо.
                Не думаю что стоит говорить «если дорос, то...» — все решают разные классы задач, и не всегда есть время даже задуматься о чем то интересном.

                В случае с треками вообще нет проблем

                Да нет, я, в данном случае, говорил о кеше, хотя бы, в сотню треков. А это не так круто каждый раз гонять пол гигабайта трафика. Особенно если это какой-нибудь 3-4g.

                Вообще не понимаю чего ожидал автор и каких обсуждений — это тривиальные и очевидные вещи

                Жаль лишь что подобных живых решений решений я видел единицы. Даже крупные порталы типа того же контакта не позволяют пользоваться частичным функционалом оффлайн.

                Ну и ещё момент, что реализация подобных фитч, по сути, разделяет функционал на 2 части — работающий только онлайн и который может работать и оффлайн. Соответственно код надо поддерживать. Внедрение этого всего стоит денег, которые не всегда есть.
                • 0
                  Да нет, я, в данном случае, говорил о кеше, хотя бы, в сотню треков. А это не так круто каждый раз гонять пол гигабайта трафика. Особенно если это какой-нибудь 3-4g.

                  Тут вопрос в том как попросить у пользователя большее количество места на сохранение и доступ в локаль. Пока не копал, но вроде как 5/50 метров это лимит. Ну, и всегда есть флэш )

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

                  Жаль лишь что подобных живых решений решений я видел единицы.

                  Потому что % такого рода пользователей КРАЙНЕ МАЛ(с). Да и пенять пользователь скорее всего будет на себя.
                  Хотя я вот сейчас занимаюсь подобным проектом — SPA + API на бэкэнде. Теоретически возможно сделать «толстый клиент», но пока посчитали что трудозатратно и не выгодно.
                  • 0
                    Тут вопрос в том как попросить у пользователя большее количество места на сохранение и доступ в локаль. Пока не копал, но вроде как 5/50 метров это лимит. Ну, и всегда есть флэш )

                    Ну я как бы это в статье описал от части :-) 5мб это лимит на LocalStorage, для других хранилищ лимиты больше. Там при достижении порога пользователю вопрос приходит на разрешение хранения оффлайн инфы.
                    Т.е. я подобное уже делал — работает хорошо.

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

                    Нет, при нормальной организации моделей эта работа абсолютно незаметна

                    Потому что % такого рода пользователей КРАЙНЕ МАЛ(с).

                    У меня за год было минимум 4 подобных случая с доставкой еды :-) А это -4 тысячи рублей продавцу.
                    У друзей знакомых бывали подобные ситуации. Они случаются, причем довольно регулярно.
                    Да, может это и не такой большой процент, но тем не менее это не только прибыль, но + к лояльности.

                    Да и пенять пользователь скорее всего будет на себя.

                    Будет на провайдера, да. Но тут ты решил его проблему — клиент доволен. В следующий раз будет знать у кого стоит брать, кто дает крутой сервис.

                    Теоретически возможно сделать «толстый клиент», но пока посчитали что трудозатратно и не выгодно.

                    Это всё от случая к случаю. Всё индивидуально. По моему опыту «толстый клиент» обходится дешевле в разработке и дешевле в поддержке. Бонусом ещё и более гибок. Но, как я и писал, это всё зависит от конкретной ситуации. Они все индивидуальны.
              • +1
                Спасибо за статью.
                Тема интересна, но выходные дают о себе знать :)

                А вообще, как мне кажется, это всё вопросы бизнеса, а не разработчика. Как заказчик решит делать, так и следует делать.
                • 0
                  Пожалуйста.
                  Вопросы бизнеса — внедрять ли подобное, разработчика — как внедрять.
                • 0
                  Вообще-то классификаця connectivity model выглядит так:

                  1. connected
                  2. occasionally disconnected
                  3. occasionally connected
                  4. disconnected (не требует подключения для работы)


                  Требования и решения разные для всех случаев.
                  • 0
                    Ну то что человек сам закрыл вкладку — понятно что тут всё ок. Я рассматривал то что у тебя под пунктом 2.
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      На первый взгляд вполне неплохо так. Покручу как время будет, спасибо.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.