Распределение нагрузки в IIS для NodeJS

Главная проблема javascript это то что он однопоточный. Эта же проблема есть и в приложения написанных на NodeJS.

Итак нам понадобится:

  • виртуальная машина с Windows Server 2012 или выше
  • установленная IIS
  • приложение на NodeJS (в примере будет использоваться фреймворк Express)
  • и «магия»

Инструкцию по установке Windows и настройке IIS описывать не буду, это можно найти в интернете. Приступаем сразу к запуску NodeJS приложения.

Краткая предыстория

Имеется проект написанный на NodeJS, которому требуется обрабатывать большие JSON файлы (около 100-200 клиентских приложений, которые передаю и принимают файлы объемом 40-80 Мб). В один момент времени может возникнуть ситуация, когда несколько пользователей одновременно сделают запрос. И в это время количество ОЗУ может не хватить (по моему по умолчанию ограничение 2 Гб). NodeJS вызовет ошибку, а IIS перезапустит пул. Можно конечно увеличить это ограничение, но рано или поздно будут возникать проблемы с производительностью.

Решение: распределение нагрузки при помощи виртуальных каталогов

Простое приложение на NodeJS

Мы будем использовать приложение, которое будет написано при помощи фреймворка express.

Файл: app.js

var express = require('express');
var app = express();
var join = require('path').join;

/*** Порт для работы приложения */
var port = normalizePort(process.env.PORT);

/*** Виртуальный каталог */
var vPath = process.env.virtualDirPath || '';

app.use(vPath + '/', express.static(join(__dirname, 'public')));

app.get(vPath + '/', function (req, res) {
    res.send(vPath);
});

app.listen(port, function () {
    console.log('Example app listening on port ' + port + '!');
});

/**
 * Normalize a port into a number, string, or false.
 * @param {any} val номер порта
 * @returns {number|boolean}
 */
function normalizePort(val) {
    var port = parseInt(val, 10);
    if (isNaN(port)) {
        // named pipe
        return val;
    }
    if (port >= 0) {
        // port number
        return port;
    }
    return false;
}

Файл: packge.json

{
  "name": "simple-express",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "args-parser": "^1.1.0",
    "express": "^4.17.1"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Файл: web.config

<configuration>
  <system.webServer>
    <handlers>
      <add name="iisnode" path="app.js" verb="" modules="iisnode" />     
    </handlers> 
    <rewrite> 
      <rules> 
        <rule name="myapp"> 
          <match url="/" />
          <action type="Rewrite" url="app.js" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Располагаем наше приложение в корневом каталоге IIS (можно расположить, где угодно, но нужно будет дополнительно настраивать). У меня оно называется arm

Проект «arm» в корне каталога

Настройка IIS

Примечание: NodeJS приложению требуются дополнительные права на исполнение, для этого нужно убедиться, что есть полный доступ для пользователя IIS_IUSRS

Полный доступ для IIS_IUSRS

Далее создаем столько пулов на сколько потоков мы хотим «разделить» наше приложение. В моем примере это будет 2.

При создании пулов не требуется исполнение CLR, нам нужен сам факт наличия, т.к. пул IIS нам и обеспечивает параллельность выполнения.

Первый пул
Два пула

После создания пулов переходим к настройки виртуальных каталогов. Для этого в приложении по умолчанию «Default Web Site» выбираем «Добавить виртуальный каталог»

Добавление виртуального каталога
  • в «псевдониме» указываем наименование приложения. Я указывал arm, arm2
  • «физический путь» указывает на каталог wwwroot/arm
  • после создания требуется преобразовать в приложение и указать соответствующий пул
Создание виртуального каталога
Преобразование в приложение
Результат настройки

Последним «штрихом» является web.config на уровне корневого каталога

<configuration>
  <location path="arm">
    <appSettings>
      <add key="virtualDirPath" value="/arm" />
    </appSettings>
  </location>
  <location path="arm2">
    <appSettings>
      <add key="virtualDirPath" value="/arm2" />
    </appSettings>
  </location>
</configuration>

virtualDirPath — дополнительная настройка, которая используется в приложении на NodeJS

...
var vPath = process.env.virtualDirPath || '';
app.use(vPath + '/', express.static(join(__dirname, 'public')));
...

Примечание: особенность настройки заключается в том, что физически в файловой системе у нас будет храниться один каталог с приложением, но IIS при помощи «виртуализации» создаст несколько

Результат

Результат работы

Ниже мы видим как IIS под каждый экземпляр виртуального каталога создала отдельный процесс

Два каталога — два процесса

Print Friendly, PDF & Email

Добавить комментарий