Заметки с тегом «nodejs»

Знакомство с AWS Lambda

Amazon Web Services Lambda — это веб-сервис, запускающий ваш код на Node.js, Python или Java в ответ на определенные события и отвечающий за автоматическое выделение необходимых вычислительных ресурсов. Функции Lambda не хранят состояние, поэтому AWS Lambda может быстро запустить столько копий функции, сколько нужно для обработки входящих событий.

Оплата производится за количество вызовов функции и время, необходимое для исполнения кода этой функции. Изучение и маленькие домашние проекты для вас могут быть совершенно бесплатными в течение года с момента регистрации в AWS.

Создание пользователя

После регистрации в AWS нужно создать пользователя и назначить ему права доступа к сервисам.

Все инструкции вы найдёте в документации AWS Identity and Access Management «IAM Users». Сохраните файл с ключами — они потребуются при настройке CLI.

Только что созданный пользователь не имеет никаких прав. Чтобы он получил доступ к созданию и запуску функций Lambda, нужно назначить ему соответствующую политику. В разделе «Working with Policies» описано как добраться до списка политик. Нам нужно добавить для пользователя политику «AWSLambdaFullAccess». Позже аналогично можно будет добавить или удалить другие политики.

AWS Command Line Interface

Почти все функции AWS доступны через веб-интерфейс, но на практике удобнее использовать консольные команды или скрипты.

Инструкции по установке утилит вы найдёте на странице «AWS Command Line Interface». После того как вы проделаете все описанные там действия, запустите команду:

aws configure

и укажите ключи пользователя, которые вы получили при его создании. Я так же выбрал регион по-умолчанию «eu-central-1». В разных регионах доступны разные наборы сервисов.

Создание функции AWS Lambda

Для начала, напишем простой скрипт, который будет возвращать строку "Hello, world!". Так же в логах мы увидим с какими параметрами вызывалась функция.

'use strict';

exports.handler = (event, context, callback) => {
  console.log(JSON.stringify(event, null, 2));
  callback(null, 'Hello, world!');
};

Сохраним этот код в файл index.js.

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

Перед тем как загрузить функцию в AWS, нужно создать роль для неё. Если у вас уже существует подходящая роль, то этот шаг можно пропустить.

aws iam create-role \
  --role-name MyFirstLambda-Execution \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }]
  }' \
  --output text \
  --query 'Role.Arn'

В ответ вы получите строку вида:

arn:aws:iam::000000000000:role/MyFirstLambda-Execution

Так как наша функция будет вести логгирование, то нужно добавить ей соответствующую политику. Иначе лог не сохранится в CloudWatch.

aws iam attach-role-policy \
  --role-name MyFirstLambda-Execution \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Вот теперь можно загрузить нашу функцию в Lambda.

zip - index.js | \
aws lambda create-function \
  --function-name MyFirstLambda \
  --runtime nodejs4.3 \
  --role arn:aws:iam::000000000000:role/MyFirstLambda-Execution \
  --handler index.handler \
  --zip-file fileb:///dev/stdin

Код функции будет архивироваться и загружаться в AWS без создания временных файлов. Так же код можно загружать через S3.

В параметре --handler указываем точку входа — название модуля (index) и метод, который экспортирует модуль.

В параметре --role указываем роль, которую мы создали для запуска функции.

Выполнение функции

Запустить функцию можно из терминала

aws lambda invoke \
  --function-name MyFirstLambda \
  /tmp/out.json && cat /tmp/out.json

Результат сохранится в файл /tmp/out.json, содержимое которого мы затем выведем в терминале.

aws lambda invoke \
  --function-name MyFirstLambda \
  --payload '{"foo":1,"bar":true}' \
  /tmp/out.json

Обновление кода и конфигурации функции

Обновляется код функции аналогично тому как она создавалась.

zip - index.js | \
aws lambda update-function-code \
  --function-name MyFirstLambda \
  --zip-file fileb:///dev/stdin

Если нужно обновить какие-то параметры конфигурации, то это делается командой update-function-configuration.

Заключение

С AWS Lambda можно легко получить масштабируемое окружение.

На практике функции вызываются по какому-либо событию. Например, запуск по расписанию, обновление таблицы в DynamoDB, появление файла в S3 или при поступлении HTTP-запроса в сервис API Gateway.

В свою очередь, код Lambda-функции может взаимодействовать с другими серверами AWS, внешними базами данных и веб-ресурсами.

Оставте свой комментарий

Шаблон тестирования Page Object

Функциональные тесты большинством воспринимаются как линейные сценарии: открой страничку, нажми эту кнопку, введи такой-то текст в поле ввода, нажми другую кнопку. Сценарии похожи на то, как действует реальный пользователь. Однако они плохи тем, что требуется повторять много однотипных действий в разных тестах, чтобы попасть в нужное исходное состояние. Если меняется содержимое станицы (например, разметка или классы у элементов), то нужно во все тестах внести соответствующие изменения.

В документации к Selenium описан другой подход — Page Object. Его особенность заключается в том, что страница представлена в виде модели с которой взаимодействует тест. Это уменьшает количество повторения кода. Доступ к элементам страницы осуществляется только в одном месте. Если поменяется разметка, то потребуется минимум изменений в коде Page Object, а тесты не нужно будет менять вообще.

Таким образом, с точки зрения кода функциональные тесты превращаются в тестирование сервиса, который знает всё о внутреннем устройстве всей страницы или её части и реализует публичные методы, выполняющие определённые действия.

Смотрите пример — https://gist.github.com/mistakster/597f110631fe8a5cde6b. Инфраструктура для запуска этого теста описана в заметке Node.js + mocha + selenium-webdriver.

Я тестирую главную страницу Яндекса. Она должна удовлетворять следующей спецификации:

Yandex home page
  √ should be valid (4857ms)
  √ should have at least 9 tabs above the search (4358ms)
  √ should have a weather widget (1562ms)

Сам тест выглядит очень просто и понятно. Вся магия скрыта в объекте Page и двух компонентах, которые должны быть на странице.

Когда мы открываем страницу, то не должны знать её адреса. Для разных окружений (локальная разработка, стейджинг, продакшин) он может быть разным. В тесте у Page Object просто вызывается соответствующий метод open(). Мы так же должны быть уверены, что страница открылась и на ней правильное содержимое. За это отвечает метод validate(). Итак, всю логику по взаимодействию с драйвером мы прячем в Page Object за компактным фасадом из методов.

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

Комментарии к заметке: 2

Node.js + mocha + selenium-webdriver

Для запуска функциональных тестов нам понадобятся:

  1. Node.js — надеюсь, он у вас уже установлен;
  2. Python 2.7.х — указан в зависимостях модуля selenium-webdriver и должен быть установлен в систему до загрузки остальных пакетов;
  3. Java Runtime Environment — среда для запуска Selenium Server;
  4. mocha — с помощью этой утилиты мы будем запускать тесты; устанавливается командой npm i -g mocha;
  5. selenium-webdriver — через него мы будем оправлять Selenium-серверу команды и получать от него данные со страницы; устанавливается в папку проекта командой npm i selenium-webdriver;
  6. Selenium server — запускает и закрывает браузер, отправляет браузеру команды и т.п.; не требует особой установки; скачиваем JAR-файл в папку рядом с тестами;
  7. Driver Provider — вспомогательный класс, в котором сосредоточен код для запуска и остановки сервера и конфигурация драйвера (например, выбор браузера для тестирования).

Когда всё перечисленное будет установлено, можно начинать писать и запускать тесты.

В каждом пакете тестов нужно создать экземпляр класса DriverProvider. Как я указал выше, с его помощью мы будем управлять сервером и драйвером.

var driverProvider = new DriverProvider();

Перед запуском пакета тестов выполняется метод before:

test.before(function () {
  return driverProvider.startUp();
});

По окончании работы тестов выполняется метод after:

test.after(function () {
  return driverProvider.tearDown();
});

DriverProvider — это хорошая точка расширения функциональности. В этом классе можно управлять конфигурацией драйвера без вмешательства в код самих тестов. Например, я указываю там браузер, в котором нужно запустить тест. Код можно слегка поправить для использования удалённого Selenium-сервера или запуск специального прокси-сервера.

В методах startUp() и tearDown() этого класса выполняются задачи по запуску и остановке соответствующих сервисов. Они должны возвращать promise-объект. По его состоянию будет определяться завершена-ли задача или нет.

Отличительной особенностью API selenium-webdriver является то, что большинство методов выполняются асинхронно. Однако, постоянно использоваться promise-объекты, чтобы обеспечить нужный порядок выполнения команд, вовсе не требуется. Для этого есть так называемые “control flow”. Внутри одного потока все команды будут исполняться синхронно по мере того как они были добавлены в поток. Это немного облегчает написание тестов. Но, если нужно получить значение из функции (например, список элементов, размеры элемента и т.д.), то без “обещаний” не обойтись.

Так же библиотека selenium-webdriver/testing тоже использует “control flow” чтобы последовательно исполнять содержимое тестов. Она дублирует методы mocha. Тест может вернуть promise-объект и он будет считаться завершённым, когда обещание будет установлено в какое-либо состояние. Если тест ничего не вернул, то он будет считаться завершенным, когда очередь заданий окажется пуста.

И в заключение команда, которая запустит на выполнение наш тест:

mocha page-spec.js

У команды mocha есть масса полезных параметров. Мне, например, нравятся отчёты в виде спецификации. Ещё можно указать максимальное время выполнения теста. Функциональные тесты, как правило, не быстрые и 2 секунды на тест (значение по-умолчанию) совсем не хватает, чтобы его выполнить.

В заключение, хочу обратить внимание на шаблон написания тестов Page Object. Этот подход позволяет выделить взаимодействие с WebDriver и повторяющиеся во время тестирования действия в изолированные классы. Практика показывает, что таким образом легче поддерживать тесты в актуальном состоянии.

Комментарии к заметке: 2

Реализация WebSocket клиента для Node.js

Для нагрузочного тестирования WebSocket сервера в проекте мы решили написать ботов, имитирующих подключения пользователей, и создать с их помощью некую активность. Ботов написал я на Node.js в виде скрипта с параметрами.

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

Работа WebSocket клиента устроена аналогично net.Socket:

// Создаётся экземпляр клиента
var WebSocketClient = require('websocket').client;
var client = new WebSocketClient();

// Вешаем на него обработчик события подключения к серверу
client.on('connect', handler);

// Подключаемся к нужному ресурсу
client.connect('ws://localhost:9000/');

В обработчик подключения в качестве аргумента передаётся объект, который может принимать и отсылать сообщения:

function handler(connection) {
  connection.on('message', function (message) {
    // делаем что-нибудь с пришедшим сообщением
    console.log(message);
  }
  // посылаем сообщение серверу
  connection.sendUTF('Hi, there!');
}
Оставте свой комментарий

Ограничение скорости загрузки по сети

Мне для тестирования бывает нужно ограничить скорость HTTP-трафика для проверки того, как ресурс работает в тех или иных условиях.

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

Устанавливается он в систему с помощью npm:

npm install -g throttle-proxy

И запускается командой:

throttle-proxy

По-умолчанию, сервер будет слушать подключения на порту 3128 и ограничивать скорость до 100 тысяч байт в секунду. Эти значения можно поменять с помощью соответствующих параметров при запуске сервера.

throttle-proxy --speed 50000 --port 9999

Остановить прокси-сервер можно комбинацией клавиш Ctrl+C в консоли, где он запущен.

Не забудьте только указать адрес 127.0.0.1 и порт 3128 (или выбранный вами порт) в настройках системы в качестве HTTP-прокси сервера.

Исходный код доступен на GitHub — throttle-proxy

Update: С подачи пользователей добавил фильтр ресурсов, для которых нужно применять ограничение скорости, и фильтр, который пропускает ресурсы без ограничений.

Комментарии к заметке: 4