Як архітектуру в реальному часі веб-програм на основі веб-сокетів?


17

У процесі розробки додатку для одиночної сторінки в реальному часі я прогресивно використовував веб-розетки для розширення можливостей своїх користувачів актуальними даними. Під час цієї фази мені було сумно помітити, що я занадто сильно руйнував свою структуру додатків, і мені не вдалося знайти рішення цього явища.

Перш ніж вдаватися до конкретики, лише трохи контексту:

  • Webapp - це СПА в реальному часі;
  • "Backend" знаходиться в Ruby on Rails. Події в реальному часі Рубі натискає на клавішу Redis, потім сервер мікровузлів відтягує його назад і натискає на Socket.Io;
  • Frontend знаходиться в AngularJS і підключається безпосередньо до сервера socket.io в Node.

На стороні сервера до реального часу я мав чітке розділення ресурсів на основі контролера / моделі, з обробкою додається до кожного. Цей класичний дизайн MVC був повністю подрібнений або, принаймні, обхідний, саме тоді, коли я почав надсилати речі через веб-розетки своїм користувачам. Зараз у мене є одна труба, куди все моє додаток стікає з більш-менш структурованими даними . І мені це здається напруженим.

На передньому кінці, головне питання - це дублювання логіки бізнесу. Коли користувач завантажує сторінку, я повинен завантажувати свої моделі через класичні дзвінки AJAX. Але мені також доводиться обробляти дані в реальному часі, і я можу дублювати велику частину бізнес-логіки на стороні клієнта, щоб підтримувати узгодженість моїх клієнтських моделей.

Після деяких досліджень я не можу знайти жодних хороших дописів, статей, книг чи іншого, що дало б поради щодо того, як можна і потрібно проектувати архітектуру сучасного веб-сайту з кількома конкретними темами:

  • Як структурувати дані, які надсилаються з сервера користувачеві?
    • Чи слід надсилати лише події на кшталт "цей ресурс оновлено, і ви повинні перезавантажити його через дзвінок AJAX" або натиснути оновлені дані та замінити попередні дані, завантажені за допомогою початкових дзвінків AJAX?
    • Як визначити узгоджений та масштабований скелет за надісланими даними? це повідомлення про оновлення моделі чи повідомлення "сталася помилка з blahblahblah"
  • Як не надсилати дані про все з будь-якого місця бекенда?
  • Як зменшити дублювання ділової логіки як на сервері, так і на стороні клієнта?

Ви впевнені, що Rails - це найкращий варіант для SPA? Rails - це чудово, але він спрямований на застосування моноліту ... ви можете захотіти модульний бекенд з роз'єднанням проблем ... Залежно від ваших потреб, я б розглядав альтернативні рамки Ruby для SPA в режимі реального часу.
Myst

1
Я не впевнений, що Rails є найкращим варіантом, але мене дуже влаштовує стек на місці, принаймні на бекенді (можливо, тому, що мені добре в цьому конкретному рамках). Мене тут хвилює більше про те, як розробити СПА з технічно-агностичної точки зору. І я також не хочу помножувати кількість мов, рамок і бібліотек в одному проекті.
Філіп Дюрікс

Зв'язаний орієнтир може мати проблеми, але він виявляє слабкість у поточній реалізації ActiveRecord. Я можу бути упередженим щодо plezi.io , але, як було зазначено в пізніших результатах бенчмарка , він забезпечує значне поліпшення продуктивності, навіть до кластеризації та Redis (який не був протестований). Я думаю, що він працював краще, ніж node.js ... Поки все не зміниться, я б використовував plezi.io.
Myst

Відповіді:


10

Як структурувати дані, які надсилаються з сервера користувачеві?

Використовуйте шаблон обміну повідомленнями . Ну, ви вже використовуєте протокол обміну повідомленнями, але я маю на увазі структурування змін як повідомлень ... конкретно подій. Коли сервер змінюється, це призводить до ділових подій. У вашому сценарії, ці події ваших клієнтів зацікавлені в цих подіях. Події повинні містити всі дані, що стосуються цієї зміни (не обов'язково всі дані перегляду). Потім клієнтська сторінка повинна оновити частини перегляду, які вона підтримує, за допомогою даних події.

Наприклад, якщо ви оновлювали біржовий біржовий пакет і змінювали AAPL, ви не хочете знижувати всі ціни на акції або навіть усі дані про AAPL (ім'я, опис тощо). Ви б тільки натиснути AAPL, дельту та нову ціну. Тоді для клієнта ви б оновили лише цю ціну акцій у поданому вигляді.

Чи слід надсилати лише події на кшталт "цей ресурс оновлено, і ви повинні перезавантажити його через дзвінок AJAX" або натиснути оновлені дані та замінити попередні дані, завантажені за допомогою початкових дзвінків AJAX?

Я б не сказав жодного. Якщо ви надсилаєте подію, вперед і надсилайте разом із нею відповідні дані (а не дані всього об’єкта). Дайте йому назву для того, який тип події це. (Іменування та дані, що стосуються цієї події, виходять за межі механічної роботи системи. Це більше стосується способу моделювання ділової логіки.) Ваші оновники перегляду повинні знати, як перевести кожну конкретну подію в точна зміна перегляду (тобто оновити лише те, що змінилося).

Як визначити узгоджений та масштабований скелет за надісланими даними? це повідомлення про оновлення моделі чи повідомлення "сталася помилка з blahblahblah"

Я б сказав, що це велике, відкрите запитання, яке слід розбити на кілька інших питань і розмістити окремо.

В цілому, ваша система зворотного зв'язку повинна створювати та відправляти події для важливих подій у вашому бізнесі. Вони могли надходити із зовнішніх каналів або від активності в самій задній частині.

Як не надсилати дані про все з будь-якого місця бекенда?

Використовуйте шаблон публікації / підписки . Коли ваш SPA завантажує нову сторінку, яка зацікавлена ​​отримувати оновлення в режимі реального часу, вона повинна підписатись лише на ті події, які вона може використовувати, і викликати логіку оновлення перегляду, коли ці події надходять. Можливо, вам потрібна логіка pub / sub сервер для зменшення навантаження на мережу. Бібліотеки існують для Websocket pub / sub, але я не впевнений, що це в екосистемі Rails.

Як зменшити дублювання ділової логіки як на сервері, так і на стороні клієнта?

Це здається, що вам доведеться оновити дані перегляду як на клієнті, так і на сервері. Думаю, вам потрібні дані перегляду на стороні сервера, щоб мати знімок, щоб запустити клієнта в реальному часі. Оскільки існують дві мови / платформи (Ruby та Javascript), логіку оновлення перегляду доведеться записати в обох. Окрім трансляції (у якої є свої проблеми), я не бачу цього шляху.

Технічний момент: Маніпулювання даними (оновлення перегляду) - це не бізнес-логіка. Якщо ви маєте на увазі використання перевірки регістру, то це здається неминучим, оскільки перевірки клієнта необхідні для хорошого користувальницького досвіду, але сервер не може довіряти їм.


Ось як я бачу таку річ структурованою.

Перегляди клієнта:

  • Потрібно записувати знімок перегляду та номер останнього перегляду події перегляду
    • Це дозволить популяризувати представлення даних, щоб клієнту не довелося будувати з нуля.
    • Для простоти може бути більше HTTP GET
  • Встановлює з'єднання через веб-розетку і підписується на конкретні події, починаючи з номера останньої події перегляду.
  • Отримує події через websocket і оновлює його подання на основі типу / даних події.

Команди клієнта:

  • Попросити зміни даних (HTTP PUT / POST / DELETE)
    • Відповідь - це лише успіх чи невдача + помилка
    • (Подія, породжена зміною, перейде через веб-розетку і спричинить оновлення перегляду.)

Сторона сервера насправді може бути розбита на кілька компонентів з обмеженими обов'язками. Той, який просто обробляє вхідні запити та створює події. Інший може керувати підписками клієнтів, слухати події (скажімо, під час роботи) та пересилати підписникам відповідні події. У вас може бути третина, яка слухає події та оновлює перегляди на стороні сервера - можливо, це трапляється навіть до того, як підписники отримають події.

Те, що я описав, - це форма CQRS + Messaging та типова стратегія вирішення типу проблем, з якими ви стикаєтесь.

Я не вніс події Sourcing в цей опис, оскільки я не впевнений, чи це ви хочете взяти на себе, чи вам це потрібно обов'язково. Але це пов'язана закономірність.


Я багато просунувся в цій темі, і вказівки, які ви дали, були дуже корисними. Я прийняв відповідь, тому що я скористався багатьма порадами, навіть якщо я не користувався всіма ними. Я опишу шлях, який я пройшов у іншій відповіді.
Філіп Дюрікс

4

Після декількох місяців роботи над архівом, я мав змогу використовувати деякі поради тут для вирішення проблем, з якими стикалася платформа.

Основна мета при переосмисленні бекенда - полягати в тому, щоб якомога сильніше дотримуватися CRUD. Усі дії, повідомлення та запити, розкидані по багатьох маршрутах, були перегруповані в ресурси, які створюються, оновлюються, читаються або видаляються . Зараз це звучить очевидно, але це був дуже важкий спосіб мислення, щоб ретельно застосовувати.

Після того, як все було організовано в ресурси, я зміг приєднати до моделей повідомлення в реальному часі.

  • Створення запускає повідомлення з новим ресурсом;
  • Оновлення запускає повідомлення лише оновленими атрибутами (плюс UUID);
  • Видалення запускає повідомлення про видалення.

У API Rest усі способи створення, оновлення та видалення генерують відповідь лише головою, HTTP-код, що повідомляє про успіх чи невдачу, та фактичні дані, що передаються через веб-розетки.

На передньому кінці кожен ресурс обробляється певним компонентом, який завантажує їх через HTTP при ініціалізації, потім підписується на оновлення та підтримує їх стан у часі. Потім представлення прив'язуються до цих компонентів, щоб відображати ресурси та виконувати дії на цих ресурсах через ті самі компоненти.


Мені здалося, що CQRS + Messaging and Event Sourcing читає дуже цікаво, але відчув, що це трохи складніше для моєї проблеми, і, можливо, більше пристосований до інтенсивних програм, де введення даних у централізовану базу даних - дорога розкіш. Але я обов'язково буду пам’ятати такий підхід.

У цьому випадку у додатку буде мало клієнтів, які одночасно працюють, і я взяв участь у покладанні на базу даних. Найбільш мінливі моделі зберігаються в Redis, якому я довіряю обробляти кілька сотень оновлень за секунду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.