#javascript #node.js
Вопрос:
Я работаю с API для анализа данных фондового рынка в NodeJS.
Программа отлично работает до примерно 30 тысяч транзакций в секунду, что нормально для более медленных частей дня. Однако во время открытия рынка он может превышать 100 тыс. транзакций в секунду, а объем кучи увеличивается до 30 ГБ , запаздывает и в конечном итоге падает, несмотря на то, что он работает на чрезвычайно быстрой машине.
Я относительно новичок в NodeJS, но узким местом, по-видимому, является приведенный ниже раздел кода, который я получил от примера клиента, являющегося расширением класса EventEmitter.
onMessage( data ){ data = JSON.parse( data ) data.map(( msg ) =gt; { if( msg.ev === 'status' ){ console.log('Status Update:', msg.message) } this.emit(msg.ev, msg) })
Есть ли более эффективный способ кодирования, который мог бы повысить скорость в 2 раза? Отдельные биты JSON довольно малы, например, следующие:
{ "ev": "T", "sym": "MSFT", "x": 4, "i": "12345", "z": 3, "p": 114.125, "s": 100, "c": [ 0, 12 ], "t": 1536036818784 }
Комментарии:
1. Ну, начнем с того, что было бы намного эффективнее отправлять одно сообщение со всеми данными, а не отдельное сообщение для каждой итерации,
data.map()
и пусть клиент сам их просматривает. Это значительно сократит количество отправляемых вами отдельных сообщений. Если в этом массиве много элементов, это может быть легко в 10 раз или более эффективно для вашего сервера (в зависимости от размера массива).2. Это имеет смысл, но я обрабатываю данные и отвечаю на них в режиме реального времени с помощью API, который передает их мне по одному в режиме реального времени. Так что это затрудняет их массовое выполнение. Это довольно большой разброс в скорости поступления данных-от одного каждые несколько секунд около 5 утра до миллиона в секунду на момент закрытия рынка.
3. Э-э-э,
data.map()
это обработка всего, что у вас есть, по одному за раз, а не оптом. Я предлагаю вам избавиться от всего этого цикла. Кроме того, если вы регулярно разбираете эти мс, вы можете накапливать их для одного клиента в течение 100 мс или 1000 мс или любой другой временной задержки, подходящей для вашего приложения, и отправлять все, что накопилось, сразу. Весь смысл здесь в том, чтобы перестать отправлять миллионы крошечных сообщений. Это чрезвычайно неэффективно для сервера.4.
map()
создает массив результатов всех функций обратного вызова. ИспользуйтеforEach()
, если вам не нужен этот массив. Это сэкономит немного памяти.5. Вы можете использовать новый итерационный подход…
for (const msg of data) { ... }
В любом случае, я бы рекомендовал найти способ разделить обработку данных на несколько хостов.
Ответ №1:
В своих комментариях я предложил вам вынуть data.map()
цикл и отправить все это сразу, а затем позволить клиенту повторить отдельные фрагменты:
onMessage( data ){ data = JSON.parse( data ) this.emit("multiMsg", data); }
Тогда у клиента было бы это:
socket.on("multiMsg", (data) =gt; { for (const item of data) { // process each item here console.log(item); } });
Если вы все еще получаете много onMessage()
звонков в режиме быстрого реагирования, вы можете накапливать их в течение короткого периода времени (время выбора зависит от приемлемой задержки для вашего приложения). Это также позволяет вам значительно сократить количество отдельных сообщений, которые вы отправляете клиенту во время большого трафика, что может быть во много, много раз эффективнее для сервера. Таким образом, если бы вы получили 100 вызовов в onMessage()
течение 50 мс, то эта модификация отправит клиенту одно накопленное сообщение вместо 100 отдельных сообщений клиенту.
Вот идея, как это может сработать:
const msgQueueTime = 500; // pick an appropriate time delay here let msgQueue = []; let msgQueueTimer = null; onMessage( data ){ data = JSON.parse( data ) if (!msgQueueTimer) { msgQueueTimer = setTimeout(() =gt; { // send data we have accumulated this.emit("multiMsg", msgQueue); msgQueue = []; msgQueueTimer = null; }, msgQueueTime); } // add data onto our queue array msgQueue.push(...data); }
Комментарии:
1. Эм… как отложить переход к ускорению обработки? Я здесь кое-что упускаю.
2. @RichN — Это позволяет накапливать входящие сообщения в течение короткого периода времени и отправлять клиенту 1 сообщение, содержащее все данные, вместо отправки 1000 отдельных сообщений клиенту. Это намного, намного эффективнее для сервера. ОП утверждает, что они часто отправляют миллионы таких сообщений, и у сервера возникают проблемы с этим. Это средство сделать этот способ более эффективным, чтобы сервер мог идти в ногу со временем. Это похоже на то, как для вас эффективнее один раз сходить в супермаркет и купить 10 товаров, чем 10 раз ходить за 1 товаром каждый раз.
3. а ну понятно. Но поскольку узел однопоточный, разве мы, вероятно, не просто перемещаем итерацию в другой бит той же программы в более поздний момент времени? У меня сложилось впечатление, что этот код находится в клиенте, и мы не отправляем эти сообщения другому процессу. Возможно, я ошибаюсь.
4. @RichN — Ну, при отправке 1000 сообщений требуется гораздо больше накладных расходов, чем при отправке 1 сообщения со всеми 1000 битами данных. Таким образом, однопоточный или нет, просто намного эффективнее отправлять меньше сообщений, содержащих несколько фрагментов данных, чем множество отдельных сообщений. Это верно для любых данных, которые вы отправляете по сети или записываете в файл и т. Д., И не имеет ничего общего с потоковой передачей.
5. Пакетный метод, на который вы подписались, сделал это. Спасибо! Мне также нужно было удалить все несущественное, происходящее после обработки каждого объекта JSON, и периодически помещать эти элементы в отдельные пакеты. После этих изменений я смог обработать до 1 миллиона транзакций за секунду. Что кажется необходимым, поскольку сегодня утром у нас было более 400 тысяч транзакций в секунду.