В Node v14 появилось множество новых интересных функций. Давайте углубимся в некоторые из этих функций, создав настоящее приложение.

21 апреля Node.js 14 был объявлен текущим выпуском. Это связано не только с улучшениями платформы, но и с новыми ключевыми словами, доступными в новом Javascript.

В этой статье вы собираетесь создать полную программу Node.js. Имейте в виду, что мы используем несколько экспериментальных функций, что означает, что этот код может сломаться в любой момент. Если вы видите, что здесь что-то не работает, оставьте комментарий ниже, и я посмотрю как можно скорее.

Наша цель - создать монитор производительности приложений (APM). Я расскажу о следующих функциях Node.js в этом приложении:

Новинки или улучшения:

  • Дополнительное объединение и нулевое объединение;
  • Intl.DisplayNames из API локализации;
  • Диагностические отчеты;
  • Флаг autoDestroy по умолчанию для Node.js Streams;
  • Экспериментальный асинхронный API локального хранилища;
  • Экспериментальный assert.CallTracker;
  • Экспериментальный ожидание верхнего уровня;
  • Экспериментальные модули ECMAScript.

Не так уж и ново:

  • Крючки производительности.

Использование Node.js v14

Однако вы, возможно, думаете о том, как установить Node.js в этой конкретной версии. Вам нужно будет установить и настроить Диспетчер версий узла или установить его с веб-сайта Node.js. Я настоятельно рекомендую вам установить NVM, чтобы вы могли переключаться между версиями без необходимости изменять всю среду. Кроме того, в этом руководстве вы будете запускать команды Unix. Если вы работаете в Windows, вы можете использовать Подсистему Windows для Linux (WSL), чтобы также использовать команды Unix.

Если у вас установлен NVM, запустите на терминалеnvm install 14.3, а затемnode -v, чтобы проверить текущую версию . Если вы знакомы с Docker и предпочитаете его использовать, я написал Dockerfile, и он может вам помочь.

APM

Как я уже сказал, вы собираетесь создать приложение для мониторинга веб-API. Наше приложение будет соответствовать следующим требованиям:

  1. Длительность запросов пользователей и идентификатор пользователя должны храниться в файле;
  2. Это должен быть агент / промежуточное ПО, который добавляет настраиваемое поведение к HTTP-запросам;
  3. В случае фатальной ошибки необходимо сохранить диагностический отчет в папке отчетов .

Начиная

Пора писать код! Перейдя в свою среду, вам понадобится пустая папка для создания программ. Начну с нуля, работаю mkdir app

Пройдя через папку app, мы инициализируем проект Node.js, установим nodemon для горячей перезагрузки, uuid для генерации уникальных идентификаторов и debug для журналов с помощью следующих команд:

cd app && npm init -y && npm i -D nodemon@2 && npm i uuid@8 debug@4

Модули ECMAScript Основная поддержка

Node.js версии 13.2.0 объявила о базовой поддержке модулей ECMAScript. С тех пор ESModules развивается. В Node.js 14.x экспериментальное предупреждение для ESModules было удалено. Это означает, что они делают огромный шаг к стабильной стадии. Однако он все еще находится в экспериментальной стадии.

Наши проекты будут запускать файлы ESModules и Javascript с использованием .jsextension, чтобы увидеть, как это работает, но вы также можете использовать файлы .mjs.

Сначала мы изменим package.jsonfile, просто добавив свойство "ключ-значение" type: "module". Этот флаг сообщает среде выполнения Node.js, что мы собираемся использовать ESModules.

Источники данных клиентов

Сегодня мы не будем фокусироваться на внешних базах данных. Чтобы упростить себе жизнь, мы будем использовать файл JSON в качестве источника данных. Создайте файл database.json в папке приложения с приведенной ниже информацией.

Создание агента APM

В корневой папке (приложении) вы создадите папку и файл следующим образом:

mkdir agent && touch agent/agent.js

В agent.jsfile мы реализуем функцию, которая расширяет поведение модуля HTTP. Скопируйте файл ниже и заполните свой agent.js

Он выполнит указанные ниже действия. В скобках я помещаю строку кода для облегчения понимания:

  1. (14) - Чтение заголовков входящих запросов и получение заголовка thex-app-id, который является текущим пользователем, запрашивающим API. x-app-id фактически является клиентомid в нашем источнике данных;
  2. (15) - Используя идентификатор, он ищет владельца в предоставленном источнике данных и извлекает данные пользователя;
  3. (17) - Установите определенный заголовок с именем x-request-id, который мы будем использовать для различения каждого уникального вызова.
  4. (18) - Мутировать входящий запрос, добавляя объект данных пользователя;

Экспериментальный assert.CallTracker

Прежде чем мы продолжим, позвольте мне представить вам другую экспериментальную функцию Node.js. Если вы знакомы с тестовыми шпионами, вам понравится эта функция. Я использовал для этого sinon.JS. Шпионы - это функции, которые записывают аргументы функций, возвращаемые значения и исключения, возникающие из запросов функций.

Однако assert.CallTracker проверяет только количество вызовов функции. Он все еще находится на экспериментальной стадии и имеет только три функции:. вызовы для записи количества вызовов функции,. отчет и .verify для проверки утверждений. Мы будем использовать его, чтобы проверить, была ли вызвана функция .setHeader, чтобы убедиться, что наш настраиваемый заголовок будет добавлен.

Настройка тестов агента APM

Создав fileagent.test.js, мы реализуем нашу тестовую программу для нашего APM Agent. Скопируйте приведенный ниже код и заполните agent.test.js

Как и раньше, в скобках строка кода для облегчения понимания:

  1. (12) - Мнимая база данных. Я просто убеждаюсь, что для тестирования нашего агента не потребуется Интернет или внешние ресурсы;
  2. (20) - Создание тела запроса с добавлением идентификатора клиента в его заголовок;
  3. (25) - Создание пустой функции setH eader для последующего отслеживания. Функция. on будет выполнена, когда HTTP Emit завершит свою работу;
  4. (30) - Завершение HTTP-модуля в нашей функции start, к которой я добавил псевдоним для InjectMiddleware в (6) из agent.js;
  5. (35) - Настройка функции отслеживания только для одного вызова, а затем (36) слияние объектов response и setHeader (из tracker);
  6. (36) - Здесь происходит волшебство. После вызова функции .emit наша функция setHeader должна быть вызвана один раз. Чтобы проверить, что все утверждения будут выполнены до закрытия программы, мы добавляем tracker.verify () в (45).

Добавление скриптов в package.json

Хотите увидеть результат? Вернемся к нашему package.json и добавим скрипты для тестирования этого приложения.

В строке 08 я добавил несколько флагов в thenodemoncommand, чтобы использовать следующие экспериментальные функции Node.js:

  1. DEBUG=agent:* - В agent.js мы реализовали log('agent:runner'), а в agent.test.js, log('agent:test'). Используя шаблон agent:*, наша консоль покажет оба тега журнала;
  2. nodemon --exec - Нам нужно будет использовать определенные флаги Node.js, поэтому я предпочитаю использовать флаги --exec и node, наложенные на него;
  3. --experimental-json-modules - позволяет импортировать файлы JSON на ESModules;
  4. --experimental-top-level-await - разрешает ожидание верхнего уровня.

Проверим?

Запустив npm run test:agent, вы могли увидеть следующий результат:

Экспериментальный API асинхронного локального хранилища

Вы когда-нибудь задумывались, как отследить запрос пользователя?

Node.js Async Hooks - это API, который позволяет нам прикреплять функции для отслеживания асинхронных событий в жизненном цикле Node.js. До того, как был представлен Async Local Storage API, мы могли создавать объекты Javascript и обрабатывать данные в памяти. Конечно, плохая идея для чего-то однопоточного, не правда ли?

Используя API Async Local Storage, он может хранить отдельные контексты, добавляя данные для запросов и угрожать им независимо. Поскольку мы должны отслеживать разных клиентов по индивидуальным запросам, это будет идеально!

А насчет Performance Hooks?

Это может быть проще! Используя Performance Hooks API, мы можем отметить, когда какое-то действие началось, закончилось, а затем измерить продолжительность.

Заключение

Вместо инкрементного кода я реализую весь agent.jscode, а затем объясню его позже, хорошо ?. Создайте файл agent.js в папке agent и вставьте в него приведенный ниже код.

Что там происходит?

Давайте посмотрим на порядок, в котором здесь произошли действия.

  1. (14–27) - Устанавливает глобальный наблюдатель за перехватом производительности. Каждый раз, когда вызывается performance.measure функция, она запускает нашего наблюдателя. Когда измерение будет завершено, данные будут добавлены в файл logger;
  2. (63) - Он инициализирует новый контекст с помощью Async Local Storage API. Кроме того, мы должны сгенерировать идентификатор запроса, чтобы убедиться, что клиенты, выполняющие одновременные запросы с одним и тем же идентификатором клиента, будут обрабатываться как разные вызовы;
  3. (31) - Как только мы инициализировали наш контекст, мы можем получить текущий контекст клиента, используя .getStore. Это выглядит волшебно, не так ли? Я не буду вдаваться в подробности того, как это работает, потому что это не наша цель, поэтому, если вы хотите глубоко понять, что там происходит, посмотрите документацию;
  4. (66) - Настраивает событие для запуска завершения HTTP-запроса. После отметки выполнения запроса он также вызовет функцию performance.measure, которая вызовет наш наблюдатель ловушки производительности на (14);
  5. (58) - Ну, а как мы узнаем, действительно ли он обрабатывает параллелизм и не имеет критических проблем с производительностью? Я помещаю туда комментарий, чтобы случайным образом выбрать либо customer 1, либо customer 2. Пусть сейчас, я объясню позже.

Видя результаты

Как и раньше, запустив npm run test:agent, вы могли увидеть в консоли следующий результат:

Диагностические отчеты

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

На нашем agent.js вы добавите следующие инструкции в начало вашего файла. Теперь, если произошло необработанное исключение или по какой-либо причине приложение вылетело из строя, у нас будет новый файл в папке ./reports.

Тестирование

Хорошо, протестировать эту функцию - непростая задача. Мы должны вызвать ошибку и отловить ее с помощью process.on глобальных событий. Ниже я помещаю, как должен выглядеть файл agent.test.js.

Что там происходит?

  1. (46–47) - Настройка расположения папки отчетов. В нем хранится, сколько файлов было в этой папке до теста.
  2. (50) - Ожидаемая ошибка после удаления ключа из объекта therequest.
  3. (52) - Он прослушивает любое глобальное неперехваченное исключение. Если тест не прошел, он также вызовет неперехваченное исключение, поэтому в (53) я просто печатаю вывод, если есть другая ошибка, не связанная с моим тестовым примером.
  4. (56–57) - Если было инициировано неперехваченное исключение, наше приложение должно создать новую запись в папке reports. Это сравнение до и после того, как произошла ошибка.
  5. (59) - Здесь происходит волшебство. Мы выдаем ошибку, после чего приложение выйдет из строя и, следовательно, запустит утверждение в (56–57).

Ой, подожди. Есть оператор ожидания верхнего уровня, не так ли?

Да, друг мой, если ты понял, что есть выражение верхнего уровня await, поздравляю тебя !! Он был выпущен как экспериментальный для Node.js v14.0.3 и позволяет использовать ключевое слово await без использования функции async по (47). Теперь, добавив флаг --experimental-top-level-await в командуnode, это произойдет.

Проверка вывода

Перед повторным запуском этого проекта мы должны создать папку с именем reports в корневой папке app. Я собираюсь добавить эту команду в thepackage.json прямо в начало скрипта thetest:agent. Также добавьте в пакет nodemon правило игнорирования файлов отчетов.

Как и раньше, запустив снова npm run test:agent, вы могли увидеть следующий результат:

Наконец, наше приложение Мониторинг производительности приложений готово к использованию! Давайте создадим веб-API, чтобы увидеть, как он работает на практике.

Создание веб-API

Проект веб-API должен соответствовать списку требований:

  1. Используя Intl.DisplayNames из Localization API, он должен использовать текущий язык пользователя для перевода входящих запросов;
  2. Он должен обрабатывать входящие запросы с использованием потоков Node.js;
  3. Он должен отображать входящие данные без использования операторов if;
  4. Он должен обрабатывать ошибки Streams и возвращать пользовательские сообщения запрашивающей стороне.

Давайте создадим нашу папку api на app и пустые файлы Javascript, выполнив следующий код:

mkdir api && touch api/index.js api/index.test.js

Давайте импортируем APM Agent и создадим простой сервер, используя приведенный ниже код:

Что там происходит?

  1. (8) - Инициализация Агента, вводящего базу данных клиента при запуске;
  2. (24–25) - Я оставил этот комментарий на тот случай, если вы захотите позже запросить API, используя cURL.

Добавление скриптов API в package.json

На package.json давайте добавим скрипт для запуска API и другие скрипты для тестирования.

Обратите внимание, что для переменной средыDEBUG задано значение app:*, которое игнорирует все журналы от Агента. Если вы хотите также видеть журналы от агента, вам нужно записать их как DEBUG=app:*,agent:*, и тогда ваша консоль будет показывать полные журналы.

Проверка вывода

Сейчас я собираюсь использовать cURL, , чтобы проверить, выходит ли агент из наших запросов. Кроме того, я выберу 1 или 2 для thex-app-id, чтобы увидеть разные результаты в файле thelogger.log. Открытие двух сеансов терминала слева запускает npm start, а справа cURL, указанное в index.jsfile.

Издевательство

Используя ту же идею, что и наш запрос cURL. Нам нужно будет создать фиктивные данные для запроса API. На theapi создайте папку под названием mocks. Мы думали, что в этой папке мы создадим два файла: request1.json и request2.json со следующими данными:

Тестирование

index.test.js должен запрашивать index.js API и проверять выходные данные. Я выложу ниже весь index.test.js файл. Создайте файл index.test.jstest, скопируйте данные ниже и вставьте их туда.

Что там происходит?

Хорошо, я знаю, что здесь много кода. Можем ли мы углубиться в некоторые из них?

  1. (14) - Функция, которая делает запросы с помощью функции Http.request и возвращает обещанные результаты;
  2. (41–51) - Тестовый пример: если пользователь говорит на португальском и имеет код валюты, он должен преобразовать код валюты в описание валюты на указанном языке. Поскольку request1 имеет код валюты BRL (бразильский реал), приложение должно переводить на French как réal brésilien;
  3. (56–64) - Тестовый пример: если пользователь говорит на португальском и имеет код валюты, он должен преобразовать код валюты в описание валюты на указанном языке. Поскольку request2 имеет код валюты TWD (новый тайваньский доллар), приложение должно переводить в Portuguese как Novo dólar taiwanês;

4. (67) - Ведь закрывает де приложение.

Перед завершением поток Node.js вызывает функцию .destroy.

Сделайте небольшую паузу, чтобы представить еще одно важное улучшение экосистемы Node.js.

Эти изменения предназначены для улучшения согласованности между API Streams, устранения двусмысленности и оптимизации поведения в различных частях ядра Node.js - https://medium.com/@nodejs/node-js-version-14-available-now -8170d384567e

Обычно мы обрабатываем потоки с помощью функций Transform, Writabe, Readable и Duplex из потоков основной модуль. Теперь, до завершения функции потока Node.js, мы можем добавить настраиваемое поведение, которое будет вызываться по умолчанию.

Дополнительное объединение и нулевое объединение

И, наконец, что не менее важно, в движке V8 Engine версии 8.1 в Node.js v14 было введено много новых ключевых слов Javascript, и я очень рад поговорить об этом.

Если вы работали с языком C #, возможно, вы видели следующий код: ob1?.ob2()?.obj3 ?? "That's not enough 😨"

Необязательная цепочка - это функция, позволяющая избежать ненужных операторов if. Посмотрите, как это было до и после добавления, ниже:

  • Раньше: if(obj && obj.obj1 && typeof obj.obj1 === "function" && obj.obj1()) {}
  • После: obj?.obj1()

Реализация бизнес-правил веб-API

Скопируем приведенный ниже код, и позже я объясню, что там происходит:

Что там происходит?

  1. (10–11) - Импорт модулей для потоков Node.js;
  2. (13) - Разбор конвейерной функции до обещанной функции;
  3. (39) - Поскольку req (запрос) является читаемым потоком, мы можем читать его данные и передавать их другим потокам и так далее. В этом конвейере он получал отдельные запросы, отображал их, а затем отправлял в res (ответ). Обратите внимание, что мы получаем данные от req.user.speaks, который был вставлен агентом APM;
  4. (15) - Функция, которая будет вызываться каждый раз, когда мы получаем запрос POST с данными клиентов. Обратите внимание, что я использую функцию Transform из потоков Node.js. Также есть функция уничтожить, которая будет вызываться перед смертью Transform;
  5. (22) - Использование новых Intl.DisplayNames для перевода валют и их возврата;
  6. (27) - Использование опциональной цепочки и нулевого объединения для отображения дополнительных данных.

Тестирование

Когда наш package.json будет готов, давайте запустим тесты приложения с npm run test:app, как показано ниже:

Сделаем там стресс-тесты?

Мы собираемся установить пакет autocannon, используя npm i -g autocannon@4

Вы помните, что мы оставили комментарий в agent.js на 58-й строке? Я собираюсь прокомментировать 57-ю строку , а затем раскомментировать 58-ю строку. Я проведу нагрузочный тест с помощью пакета autocannon и посмотрю, имеет ли смысл наш logger.log файл. Для этого скопируйте приведенную ниже команду и вставьте ее в консоль после запуска приложения с использованием npm start.

Запуск его

Хорошо, посмотрим на практике, работает ли это! Выполнив все приведенные выше операторы, вы могли увидеть аналогичный результат, как показано ниже:

Это все люди

Мы сделали полный монитор производительности приложений, используя новейшие функции Javascript и Node.js. Также используются расширенные концепции Node.js, такие как параллелизм, потоки Node.js, тестирование и т. Д. Было потрясающе быть с вами и поделиться некоторыми знаниями, которые у меня есть.

Я желаю вам всего наилучшего и надеюсь, что вам понравился этот контент. Пожалуйста, поделитесь им в своих социальных сетях и помогите мне расти, оставив свой комментарий ниже.

Об Эрике Венделе

Я представлял доклады на конференциях и создавал онлайн-курсы обучения в Бразилии и за рубежом, и это было здорово!

Я собираюсь выпустить бесплатный учебный курс по Освоению потоков Node.js, отметьте это 🎉

Кроме того, если вы хотите следить за моим последним контентом, у меня есть несколько ссылок, которые должны вас заинтересовать:



Ссылки

Увидимся 🍻