Главная проблема 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
Настройка IIS
Примечание: NodeJS приложению требуются дополнительные права на исполнение, для этого нужно убедиться, что есть полный доступ для пользователя 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 под каждый экземпляр виртуального каталога создала отдельный процесс