Архитектура приложения на WebSocket
От: Faland США  
Дата: 30.08.20 03:39
Оценка:
Всем привет!
В рамках некой десктопной платформы, которая потихоньку становится клиент-серверной, есть приложение для рисования всяких графиков.
Типа такого:


Т.е. есть всякие данные в базе и в аппликухе надо их рисовать, в зависимости от того какие данные и какой тип графика, какие плагины подключены итп.
Изменения могут быть инициированы юзером через UI, так и бэкендом — через API (его используют другие приложения, скрипты итп).
Основной юз кейс пока — десктоп, это не классическое веб-приложение, здесь соответствие 1-1, т.е. типа один сервер — один клиент. По сути — сервер может работать вообще без клиента, когда скрипты прям на нем запускаются для модификации данных и ViewModel.

На данные момент архитектура выглядит как-то так:

Frontend (UI): — Javascript+dodjo в CEF/Chrome/QtWeb (разные клиенты могут быть).
Transport: WebSocket, есть pub/sub Messaging-слой, с удобным API на фронте и бэке, очереди сообщений, каналы, JSON, все дела. Бэк и фронт кидают друг в друга JSON-ы.
Backend: C++, API, база, ViewModel


Проблема: асинхронность обработки сообщений на фронте.

Например, нужно сменить тип графика на карту и некий маршрут нарисовать.
Запускаем скрипт на бэке:
api.changeView(1,1,’map’);
api.plotData(1,1,signal1ID);

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

Операций много всяких разных, кучи всяких виджетов и режимов.
На данный момент все подпирается всякого рода костылями, хватает трудновоспроизводимых багов с кривой отрисовкой/неотрисовкой того что нужно итп.

Варианты решения:
1) Асинхронная очередь команд на фронте, например на промисах.
Все переписать с promise chaining, чтоб хотя бы в рамках отдельного окна операции шли друг за другом.
Проблема: фронт-енд кода дофига (сотни тысяч строк JavaScript кода, обработчики разных каналов распихали по разным углам), сложность системы вырастает, индусы-фронтендеры сопротивляются.

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

PS: с Web-девелопментом на Вы, и то на стороне бэкенда, так что может-что то очевидное упускаю.

Есть ли какие-нибудь паттерны для разработки подобных приложений, на WebSocket или вообще? Порекомендуйте что почитать-погуглить.
Что еще можно придумать чтобы подружить синхронный бэк и асинхронный фронт?
Re: Архитектура приложения на WebSocket
От: Funny Rabbit Россия  
Дата: 31.08.20 06:58
Оценка:
Здравствуйте, Faland, Вы писали:

Мы такое делали для отрисовки данных с печей (АСУТП). Делалось через JavaApplet.
То что меня не убивает, делает меня умнее.
Re: Архитектура приложения на WebSocket
От: L.K. Марс  
Дата: 31.08.20 07:20
Оценка: 2 (1) +1
Нужно просто разделить фронт на части:
1. функция загрузки данных с бэка, сохраняющая/обновляющая полученные данные в локальном объекте
2. функция, выводящая график на основе данных локального объекта
3. управляющая часть, проверяющая состояние системы (загружены ли данные, загружены ли модули) и запускающая (при необходимости) первые две функции.

"Пустой карты" быть не должно. Если данные или модули ещё не загружены, тогда должна оставаться предыдущая карта (с каким-нибудь крутящимся шариком, обозначающим загрузку данных).
Re[2]: Архитектура приложения на WebSocket
От: Faland США  
Дата: 03.09.20 16:09
Оценка:
Здравствуйте, L.K., Вы писали:

LK>Нужно просто разделить фронт на части:

LK>1. функция загрузки данных с бэка, сохраняющая/обновляющая полученные данные в локальном объекте
LK>2. функция, выводящая график на основе данных локального объекта
LK>3. управляющая часть, проверяющая состояние системы (загружены ли данные, загружены ли модули) и запускающая (при необходимости) первые две функции.

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