Как стать автором
Поиск
Написать публикацию
Обновить
13.32
Форсайт
Разработка аналитических и мобильных решений

Бизнес требует стабильной работы мобильного приложения у всех пользователей. Что делать?

Уровень сложностиСредний
Время на прочтение7 мин
Количество просмотров562

Как загружать большие объемы данных? Часть 1.

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

Хотим рассказать, как мы в команде «Форсайт. Мобильная платформа» сделали синхронизацию больших объемов данных, чтобы это работало, в том числе, на ТСД (терминал сбора данных). Для экономии батареи ТСД специально снабжают слабыми процессорами. Весь подбор инструментов и алгоритмов мы уже апробировали в продукте «Форсайт. Мобильная платформа» (ФМП).

Специфичные условия для мобильного приложения начнем РАЗБИРАТЬ С КРЫШИ, так будет проще подобраться к существу вопроса. В среднем, отличие общедоступных приложений от бизнес-приложений – в объеме потребляемых данных. У бизнес-приложений объем данных значительно больше. Но, как водится, ожидание бизнес-пользователей от приложения точно такие же, как и у всех: приложение должно работать не просто быстро, а моментально. А это значит, что нужно найти особые техники по ускоренной передаче данных. При подборе технологий для транспорта данных нам хотелось получить:

А) Стабильный механизм передачи данных.

Б) Самый быстрый/производительный протокол из возможных.

Поскольку мы производим спецшину по транспорту данных, для нас это означает, что мобильный пользователь будет обращаться в наш инструмент за «чемоданом» данных, и мы должны как можно быстрее передать ему этот «чемодан» целиком. У разработчиков возникает вопрос — насколько большой может быть «чемодан»? Насколько будет нескромен кейс, который нужно будет тащить? За ориентир мы взяли ½ миллиона записей табличных данных.

У вас может возникнуть вопрос: зачем тащить ½ миллиона записей на мобильное устройство?

ВЫ АРХИТЕКТУРНО НЕ ТАК ДЕЛАЕТЕ! Ведь можно подгружать данные по мере необходимости.

Сразу ответим — мы с этого начинали. Подгрузка данных — требует стабильного подключения к сети, это online‑приложение, у которого вскрылись свои проблемы.

70% пользователей сообщили: «нормально работает».

30% пользователей написали: «приложение работает плохо, зависает и не грузит».

В online время реакции приложения на действия пользователя не ровное, у многих пользователей есть выбросы в большую сторону. Увы, чистый online нам не помощник. Нужно обеспечить одинаковую работу у всех пользователей. Такова специфика бизнес‑приложений.

Снова смотрим на архитектуру перегрузки ½ миллиона записей. Будем исследовать, какой протокол даёт приемлемую надёжность и скорость. Для этапа загрузки БОЛЬШИХ данных мы стали применять термин «Первичная синхронизация». Процедура может быть сверхтяжёлая, и всё равно было нужно, чтобы она проходила гладко, не падала из‑за переполнения памяти, какой бы объем ни тащили в мобильное приложение.

Проводя исследования и сравнения различных форматов и протоколов, мы выделили:

  1. Большие объемы способен передать потоковый алгоритм. Он же показал низкую чувствительность к объемам данных.

  2. Максимальную стабильность в разных условиях связи имеет потоковый алгоритм передачи. Так как есть механизм дозагрузки пакета.

  3. За счёт объявления в заголовке структуры данных и далее передачи массива значений через разделитель (без сжатия GZip), мы увидели сопоставимый размер данных, передаваемых по сети.

Что получаем? Давайте сравним и подытожим:

Распространённый подход

(простой в реализации JSon)

Потоковый

(делать сложнее)

Цепочка действий 

●  На сервере: вычитка данных с БД

●  На сервере: сериализация в JSon для передачи данных по интернет-каналам.

●  Передача по сети Internet

●  На телефоне: десериализация (конвертация в команду SQL)

●  На телефоне: запись в БД.

 

 

●  На сервере: вычитка данных с БД

●  Разделение объема на пакеты

●  На сервере: сериализация пакетов для передачи данных по интернет-каналам (Приведение к формату).

●  Как первый пакет готов Передача по сети Internet.

●  На телефоне: десериализация пакета (конвертация в команду SQL)

●  На телефоне: запись в БД. 

Вывод

Все шаги идут последовательно, следующий шаг выполняется только после успешного завершения предыдущего. Легко делать, но можно отметить довольно быструю деградацию с ростом объёма.

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

Сравнение подходов
Сравнение подходов

Посмотрите на схему в точку «А» на мобильном устройстве. Изначально тут мы с благими намерениями сделали несколько лишних вычислений, которые потом пришлось исправлять.

Перечислим лишние, если вы решите повторять алгоритм:

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

  2. Собирали полный пакет ответа в промежуточном кеше, чтобы запустить запись в БД в рамках отдельной операции вставки. Узнали, что запись на файловую систему — относительно медленная операция.

  3. Проводили на устройстве конвертацию формата из JSon в SQL команду.

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

  1. Внутри формата JSon на сервере ФМП мы стали формировать записи в нотации SQL языка, чтобы не делать этого на мобильном устройстве, а сразу брать пакет и делать вставку в БД.

  2. Возобновление дозагрузки стали делать по ID номеру строки, внутри пакета, перестали считать номера байтов и стали использовать ID номера строки.

  3. Отказались от промежуточного формирования файла.

Мы рекомендуем избегать записей в промежуточный файл. Если задача записать в БД, проектируйте вставку (insert) без промежуточных вычислений и действий. В этом случае вы, как и мы, приблизитесь к скорости, фактически равной ширине предоставляемого канала.

У ФМП сложилась схема из двух частей:

А) серверная часть умеет отправлять данные в потоке
Б) мобильный фреймворк умеет принимать поток и складывать его в БД на телефоне.

Посмотрите на схему ниже.

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

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

Посмотрите листинг кода на Kotlin для Android Фреймворка. Под столь компактный код упакованы:

  1. Аутентификация

  2. Создание БД

  3. Создание структур в БД под хранение объекта данных

  4. Обработка входного потока данных

  5. Чтение данных из БД.

val fmp: FMP = FMP.Builder() // Создать конструктор FMP.
.api(FMP.API.V2) // Указать версию API сервера.
.address("https://HOST") // Адрес сервера платформы.
.environment("ENVIRONMENT") // Среда на сервере.
.project("PROJECT") // Проект внутри среды.
.username("USERNAME") // имя пользователя.
.deviceID("device_id") // Указать ID устройства.
.storage("/path/to/storage") // Указать рабочую директорию.
.build() // Создать FMP.

val auth_password: FMPResult = fmp.user.auth("password") // Аутентификация по паролю.

val resource: FMPResource = fmp.resource
.name("...") // Указать название ресурса на сервере.
.params("...") // Указать параметры ресурса.
.cacheByParams(true) // Использовать кэширование по параметрам.
.delta(true) // Использовать дельту.
.filter(true) // Использовать фильтрацию FMPQuery.
.build() // Получить FMPResource.

val download: FMPResult = resource.download() // Потоковая загрузка данных ресурса и сохранение в БД на телефоне.

val tableData: FMPResult>> = resource.database.select("SELECT * FROM mTable;") // 

Получение данных из БД на телефоне.

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

Взгляните на схему, на ней за единицу времени удвоили производительность, можно запустить ещё несколько параллельных потоков, и тогда будет в три раза быстрее и так далее x4, x5…

Что можем порекомендовать: первым на загрузку ставьте самый большой по объёму справочник. Параллельно запрашивайте цепочку из меньших по объёму.

В схеме с многопотоковой загрузкой часто бывает, что время на загрузку коррелирует с объёмом самого большого справочника.

Ниже приведём
package ru.fsight.fmp.test

import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.Assert
import ru.fsight.fmp.FMP
import ru.fsight.fmp.FMPQuery
import ru.fsight.fmp.FMPResource
import ru.fsight.fmp.FMPResult

class SandboxTest {

@Before
fun setUp() {
}

@After
fun cleanUp() {
}

@Test
fun `Initialization FMP`() {
	val fmp: FMP = FMP.Builder()
		.address("http://mobilefmp.dev.fs.fsight.world")
		.environment("env_denis")
		.project("proj_test")
		.api(FMP.API.V2)
		.deviceID("denistest")
		.storage("./test")
		.username("test")
		.build()
}

@Test
fun `User authentication by password`() {
	val fmp: FMP = FMP.Builder()
		.address("http://mobilefmp.dev.fs.fsight.world")
		.environment("env_denis")
		.project("proj_test")
		.api(FMP.API.V2)
		.deviceID("denistest")
		.storage("./test")
		.username("test")
		.build()
	val auth: FMPResult = fmp.user.auth("testtest")
}

@Test
fun `Getting the resource data manually`() {
	val fmp: FMP = FMP.Builder()
		.address("http://mobilefmp.dev.fs.fsight.world")
		.environment("env_denis")
		.project("proj_test")
		.api(FMP.API.V2)
		.deviceID("denistest")
		.storage("./test")
		.username("test")
		.debug(false)
		.build()
	val auth: FMPResult = fmp.user.auth("testtest")
	val resource_1: FMPResource = fmp.resource
		.name("GetPlanningPeriodsCount")
		.params("{\"Count\":500000}") // 1C Basic
		.build()

	val resource_2: FMPResource = fmp.resource
		.name("GetPlanningPeriodsCount_2")
		.params("{\"Count\":500000}")
		.build()

	val resource_3: FMPResource = fmp.resource
		.name("GetPlanningPeriodsCount_3")
		.params("{\"Count\":500000}")
		.build()

	val start = System.currentTimeMillis()
	var total1 = 0L
	var total2 = 0L
	var total3 = 0L

	val t1 = Thread {
		val start1 = System.currentTimeMillis()
		val Download_1 = resource_1.download()
		total1 = System.currentTimeMillis() - start1
	}
	val t2 = Thread {
		val start2 = System.currentTimeMillis()
		val Download_2 = resource_2.download()
		total2 = System.currentTimeMillis() - start2
	}
	val t3 = Thread {
		val start3 = System.currentTimeMillis()
		val Download_3 = resource_3.download()
		total3 = System.currentTimeMillis() - start3
	}

// Parallel //параллельная
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()

// Seq //последовательная
//t1.start()
//t1.join()
//t2.start()
//t2.join()
//t3.start()
//t3.join()

	val total = System.currentTimeMillis() - start
	println("Total = ${total}, 1 = ${total1}, 2 = ${total2}, 3 = ${total3}")

}

}

Хотим сказать, что с таким простым транспортом можно сконцентрироваться на создании интерфейса бизнес-приложения.

  • Затрат времени почти нет, код компактный и понятный.

  • Не нужно тратить время на апробации подходящих решений для надёжной загрузки больших объёмов.

  • Стабилизация приложения также занимает меньше времени. А это, как вы знаете, наиболее эмоционально напряжённая часть проекта. Из-за возможных остановок сервиса и перевыпуска релизных сборок.

Как итог — бизнес‑пользователь получает приложение быстрее, а значит дешевле.

Желаем всем добра и высокой скорости передачи данных.

Теги:
Хабы:
+3
Комментарии6

Публикации

Информация

Сайт
www.fsight.ru
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия