Использование websockets vs использование ежесекундных API вызовов к серверу

Всем привет,

Есть такой вопрос, может кто в теме.
Есть разработанное веб приложение. Идея приложения: в приложении публикуются новости о компаниях, представленных на бирже акций, типа Google и т.д., юзер видит текст новости и рост или падение акций компаний, имеющих отношение к этой новости. Использованные технлогии: 1) Front-end: Angular2, и разные библиотеки, 2)Backend: Laravel5, PHP; 3) Другие: БД - MySQL, сервер Apache2.

Суть вопроса: в начале проекта я предлагал использовать веб-сокеты, но мы от них отказались, так как эксперты сказали что использование websockets, как таковое, сильно нагружает сервер. Я так понимаю что имелось ввиду то, что веб сокеты - это непереывное HTTP соединение и для каждого пользователя постоянно гоняются пустые HTTP запрос/ответ , и если условно говоря постоянно приложением пользуются 500 человек, то сервер это сильно будет перегружать.

В результате, поскольку в деле поставки информации для бизнесменов и биржевых маклеров очень важна скорость, то вместо использования вебсокетов, приложение шлет каждую секунду API запрос к бэкенду. Также администратор приложения может сделать некие изменения в приложении, которые должен сразу увидеть обычный пользователь приложения, поэтому дополнительно мы еще шлем один запрос в 5 секунд, чтобы проверить изменил ли администратор какие-то настройки приложения или нет.

Получается, что сервер сильно перегружается у нас, ресурс который используется больше всего - это CPU, и использует его в основном MySQL БД, примерно 1 CPU может обслуживать 5 юзеров.

Понятно, что абстрактно сложно сказать, но все таки, можно ли примерно оценить, стоит ли использовать для такого приложения вебсокеты или нет? Ведь если бы мы использовали вебсокеты, то кртически важная информация пересылалась бы юзеру своевременно и с небольшими ( по идее ?) затратами на серверные ресурсы. Примерно 1-2 новости появляются в 1-2 минуты, и нужно слать инфу когда админ что-то изменил, тоже может 1 раз в от 5-10 минут до 1 раз в 1-2 суток, что совсем немного. Основная нагрузка на сервер при
использовании вебсокетов – это как я понимаю, и писал выше это пересылать пустые HTTP запрос/ответ.
Клиент не доволен – приложение в результате работает медленно. Переписать на вебсокеты и проверить в реальности, как будет, никто не даст – объянить руководству такую необходимость сложно и бюджет проекта ограничен. Все -таки, абстрактно , можно ли прикинуть что для такого приложения, как я описал выше, будет лучше использование вебсокетов (в плане меньшей нагрузки на сервер и удешевления ежемесячной платы за сервера - мы сейчас используем AWS) или слать запрос к АПИ 1 раз в секунду ?

Заранее спасибо за дельные комментарии )

В ситуации есть несколько моментов:

Выходит что каждый запрос к серверу проходит по цепочке apache → php → mysql. Если вы реализуете long-polling (отсылку запросов каждые n секунд), то нужно подумать то как настроить сервер чтобы он отвечал “403” заголовком, с правильно настроенными заголовками “expires” когда запрашиаемый ресурс действительно не изменился.

Гоняющиеся запросы-ответы - это имено то что в описанной системе происходит сейчас. Вебсокеты дороги количеством потребляемой памяти, но не CPU. Плюс apache и php традиционно не ловки в работе с вебсокетами. Попробуй отказаться от apache в пользу nginx (или только вебсокеты проксировать через nginx).

Читаю что это классический случай когда вебсокеты использовать нужно.

100% что у вас со временем появятся задачи, когда надо будет пушить с сервера данные на клиент (например при синхронизации настроек пользователя, когда тот меняет настройку на другом устройстве). И чем эта история закончится? Добавлением еще одного поллинга?

В общем хватай вебсокетную библиотеку для php, пускай вебсокетное соединение не через апач, а через nginx. Напиши потом как получится.

2 лайка

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

1 лайк

Основным посылом появления WebSocket и было уйти от ежесекундный AJAX запросов.

Текущий подход может привести просто к отказу, что php не сможет обрабатывать все запросы, что он там держит 10к. одновременно. Не увидел рассуждений о масштабировании бекенда, если AWS это сделает то ок.

Так же и не понял по WebSocket, его по-любому прийдет подымать на отдельном сервере, тогда все будет летать, я имею ввиду node.js + websocket.io на отдельном сервере, тогда будет держаться 100к. подключений по сокету и уже через rabbitmq делать связку node.js + backend php в таком ключе можно масштабировать бекенд который будет содержать нужное количество воркеров.

Если все дело в “подешевле”, то оставляйте как сейчас пока не упретесь во что-то. И оптимизируйте то что есть, уверен вы это и так сделали, но все же если вся загрузка MySQL проверяйте оптимальность запросов и индексов, кешируйте результаты; не особо разбираюсь но может шард надо создать в БД что бы в мастер писалось со слейва читалось только. Запрос через 5 сек, можно объединить с 1 секундными, что бы там возвращались дополнительно нужные параметры, уже -1 запрос :-)

2 лайка

Привет,
Я на этом проекте front-end разработчик, бэкендом другой девелопер занимается. Просто возникли проблемы – приложение иногда очень сильно тормозит и не совсем понятно где причина (FE или BE). Я со своей стороны страрюсь разобраться в проблеме.
Приложение раньше было на VPS, недавно перешли на AWS. Для базы данных собираемся использовать RDS, этот сервис поддерживает масштабирование, но это уже дело бекендщика и сисадмина нашего.

Спасибо большое за информацию, буду дальше изучать эту тему )

Всем привет!
UPDATE:
Ипользовать вебсокеты мои менеджеры отказались. Сейчас подключили дополнительных разработчиков и пытаются оптимизировать бэкенд. Раз об этом заходила речь выше, то скажу что структура бэкенда сейчас такая: бэкенд использует AWS,

  • есть 3 ноды (инстанса) запущеные постоянно, управляемые Scaling group, если загрузка CPU превышает 60%, то создаются новые ноды автоматически. MySQL установлен на всех этих трех нодах и получает read запросы;
  • RDS инстанс ипользуется для получения Write запросов, так что запросы на чтение и на запись к БД разделены;
  • В S3 хранилище мы храним кратинки и xml файлы.

То что я сделал для ускорения front-end приложения:

  • мы используем кастомный скролбар для всего приложения perfect-scrollbar. Оказалось, что при большом количестве DOM элементов новостей в поле прокрутки Angular2 компонет ngx-perfect-scrollbar тормозит. Выяснилось, что если использовать чистый джаваскриптовый компонент perfect-scrollbar без обертки Angular2, то такой скроллбар работает быстрее
  • в поиске у нас можно искать биржевые новости по названию компании на бирже (как AAPL для Apple). И вот таких компаний на бирже в США около 5000. В поиске был реализован data binding и выводятся как подсказка сответствующие компании. Реализация с выводом 5000 ДОМ узлов при каждом нажатиии клавиши на клавиатуре сильно тормозила. В результате был реализован lazy loading: при каждом нажатии клавиши вывоится только 7 ДОМ узлов(названий компаний), а отсальные при необходимости подгружаются;
  • Была проблема, что когда вводишь текст в любой из input’ов, была задержка большая между нажатием клавиши и появлением буквы в поле ввода. Я во все компоненты FE приложения добавил changeDetection: ChangeDetectionStrategy.OnPush и реализовал кастомное changeDetection. Эта проблема исчезла.
  • Еще была проблема, при прокрутке вниз большого числа новостей, поле прокрутки начинало подтормаживать. В начале был реализован lazy loading для новостей и они подгружались по мере надобности, но это не решало данной проблемы. Я пришел к выводу что кастомный скролбар тормозит если одновременно в поле прокрутки находится 200 элементов и болле. Поэтому я реализовал двунаправленный lazy loading, чтобы в поле прокрутки не находилось более 40 ДОМ элемнтов(новостей) одновременно, новости теперь подгружаются и при скролле вниз и при скролле вверх.
  • Была такая проблема разные компоненты приложения почти одновременно вызывали одно и тоже АПИ. Я разработал front-end cache для АПИ запросов, ответы на HTTP запросы теперь зранятся в течении 10 секунд. Была использована this.http.get(URL).share(), чтобы не посылать одинаковые запросы одновременно, и был послан только один АПИ запрос. Кэш используется только для нечувствительных данных, АПИ запросы для получения новых новостей и АПИ для логина не кэшируются;
  • Была проблема, что прокрутка новостей в поле с нвостями делается медленно. Часть самого приложение реализовано в виде Дашбордов, есть несколько (до 8) полей прокрутки отображаемых одновременно. Для более плавной прокрутки, я реализовал особый КЭШ, в фоне для каждого модуля (поля с новостями) подгружаются 200 новостей, и при прокрутке вниз польлзователем, новости уже берутся из КЕШа. При необходимости делается дополнительный запрос в фоне – берутся след 200 новостей. В реультате прокрутка новостей стала полее гладкой и работатет быстрее.

Проблемы которые отсались:

  • Сейчас мы делаем нагрузочное тестирование приложения. long-polling (отсылку запросов каждые n секунд) запросов стало слишком много и они ложат сервера даже при 25 одновременно работающих юзерах. Команда сейчас пытается улучшить бэкенд, чтобы решить проблему.
  • для стаических ресурсов (картинки) я добавил .htaccess файл с настройками, чтобы картинки кешировалиьс браузером.
    По поводу кеширования для XHR (API запросов), чтобы сервер отвечал “403” заголовком, не совсем понятно как это сделать. Я так понял backend приложение должно внутри у себя кэшировать ответы на API запросы, и при очередном API запросе, должно лезть в БД, опять получать нужные данные, потом сравнивать вновь полученные данные и закешированные данными, если они совпадают, то отвечать заголовком “403”, если данные разные, то возвращать новые данные. Верно я понял идею?

В общем, пока так …

1 лайк

Один в один делали такой же трюк чтобы отображать прокрутку для 10к ваютных пар.

Мало информации чтобы делать конечный вывод, но из того что написано, решение кажется странным. Привносить такую завязку на метод (тайпскрипт конечно упростит ситуацию если он у вас есть) в комопнентах. Я бы стремился вынести тормозящую часть из рендера (т.е. обновлять комопнент сразу, а логику триггерить позже или в worker-e).

Чувакам “привет”. У них будет чем заняться после релиза. Как ты верно написал:

мы делаем нагрузочное тестирование приложения. long-polling (отсылку запросов каждые n секунд) запросов стало слишком много и они ложат сервера даже при 25 одновременно работающих юзерах.

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

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

Верно это один из вариантов. Он не звучит как хорошая оптимизация. Лучше начните с игры с заголовками expires. Некоторые ресурсы устаревают через 5 секунд, и если за ними клиент хочет отправить 100 запросов в 5 сек, то большинство запросов не дойдут до сервера из-за заголовка.

1 лайк