DC Metro Pro: Node.js и Socket.IO для данных в реальном времени

Информация о прибытии поездов в реальном времени с суперзарядкой

Так в чем проблема?

Управление транзита столичной зоны Вашингтона (WMATA) показывает информацию о прибытии поездов с интервалом в 20 секунд. Это означает, что если вы являетесь постоянным клиентом Metro, вы, вероятно, просматриваете информацию старше 10 секунд!

❌ U n a c c e p t a b l e.

Я имею в виду, это 2017 год, верно? У кого есть время на 20 секунд старые данные! Следующее, что вы знаете, они скажут нам, что лучше проложить туннели для автомобилей под землей, чем летать на них! Балдердаш!

Хорошо, давайте рассмотрим, как приложение и веб-сайт DC Metro Pro настраиваются так, чтобы запрашивать данные WMATA как можно быстрее.

Приложение и веб-сайт поддерживаются небольшим сервером Node.js, который передает пользователям информацию о поездах в реальном времени. Ниже приводится приблизительное описание того, как работает система.

На диаграмме выше вы можете видеть, что сервер Node.js, по сути, является слоем кэширования между клиентами и WMATA. (1) показывает HTTP-запрос, опрашивающий WMATA API, (2) показывает, что socket.io отправляет новые данные клиентам, которые этого хотят.

Почему это так?

Скорость API WMATA ограничена 50 000 запросов в день. С одним сервером, выполняющим запросы, мы можем использовать эти 50 КБ, чтобы все пользователи DC Metro Pro были в курсе последних событий. С небольшой математикой:

86,400 seconds in a day / 50,000 requests = 1.728 seconds per request

На практике я добавил небольшой буфер и делал один запрос каждые ~ 2,5 секунды (почему см. Раздел «Несколько конечных точек» ниже). В результате пользователи приложения и веб-сайта DC Metro Pro получают последнюю информацию в 8 раз быстрее, чем на веб-сайте WMATA.

Вдобавок ко всему, использование соединения WebSocket по сравнению с HTTP имеет свои преимущества в скорости и накладных расходах сети. Вокруг довольно приличная система!

Но вы не можете просто продвигать новые данные

Если клиенты получали новые данные только тогда, когда сервер Node.js получил ответ от API WMATA, то при подключении нового клиента к сети между запросами на опрос возникла задержка в 2–3 секунды.

❌ U n a c c e p t a b l e.

Чтобы клиенты чувствовали себя мгновенно, сервер Node.JS принимает разовые запросы данных (которые он очень быстро извлекает из кеша в памяти). Этот аспект больше похож на стандартную конечную точку REST, где клиент отправляет запрос и получает ответ, а не на метод прослушивания «дайте мне знать, когда у вас появятся новые данные». Вам нужно и то, и другое, чтобы иметь полную систему.

Выше вы можете видеть, когда клиент отправляет запрос данных в реальном времени для набора станций (обычно что-то вроде['A32', 'B11']), сервер захватывает данные из кеша и отправляет их обратно через отдельный emit для каждой станции.

Несколько конечных точек для синхронизации

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

Просто проверив мою математику, самое большее, что сервер будет делать 37 440 запросов в день.

(86,400 / 2.5) + (86,400 / 60) + (86,400 / 60) = 37,440 requests per day

Таким образом, примерно 75% распределения запросов используется для производственного сервера. 25% -ный буфер предназначен для развития и для того теплого ощущения, что вы находитесь в полной безопасности.

setInterval против setTimeout

Вы можете заметить одну вещь: код синхронизации не использует setInterval, несмотря на то, что функция упоминается как loopWithInterval. Это связано с тем, что использование setInterval может вызвать накопление и перекрытие запросов; представьте, если бы один запрос завис, а следующий перед ним вернулся. Это быстро попадает на плохую территорию! В моем случае самым простым решением было Good Enough ™. Мы просто ждем ответа от WMATA, а затем отправляем новый запрос по истечении заданного интервала времени. Это может привести к «плавающему», поскольку окна запроса сдвигаются вперед на задержку предыдущего запроса. Мы могли бы это довольно легко объяснить, но ¯ \ _ (ツ) _ / ¯.

Для более технического взгляда на эту проблему (но наоборот) посмотрите, как Figma обрабатывает ограничение скорости: https://blog.figma.com/an-alternative-approach-to-rate-limiting-f8a06cf7c94c.

Графики!

С момента запуска этого проекта WMATA добавила некоторые графики и информацию об использовании, поэтому я поделюсь ими ниже. Это использование за последние 90 дней.

Моя предыдущая математика, кажется, подтверждается! Ежедневно мы принимаем около 31 000 тысяч звонков. Если вы так склонны, вы можете количественно оценить, как задержка запроса влияет на количество звонков, которые мы делаем! Аккуратно.

Надеюсь, вам понравился бэкэнд, на котором работают приложение DC Metro Pro и веб-сайт. Спасибо за чтение!

✅ A c c e p t a b l e

- Майк Суровец