Построении модульной системы на основе Nodejs

в 18:16, , рубрики: Песочница, метки: ,

Основные требования

• легкость подключения модулей
• стандартная структура URL
• многоязычность
• автоматическое принятие изменений
• использование возможностей многопроцессорной системы
Система построена на базе Express. Для облегчения написания кода используется модуль wait.for

Файловая структура

  • root
    • routes
      • mod_api
        • test.js
      • api.js
    • views
    • public
    • app.js
    • Server.js

Демонизация сервера под линукс

В нашем проекте был использован модуль forever.
-w — позволяет изменять модули без прямой перезагрузки сервера. Forever следит за изменениями и перегружает сервер по надобности.
-l ведет в пустоту, так как было решено, что два лога — это черезчур.

forever start -a -w -l /dev/null -o out.log -e err.log Server.js

Использование возможностей многопроцессорной системы

Запуск множества процессов посредством модуля «cluster» для распределения нагрузки между ядрами.

//файл Server.js
var cluster = require('cluster');

var workerCount = require('os').cpus().length;

cluster.setupMaster({ exec: "app.js" });

// Fork workers.
for (var i = 0; i < workerCount ; i++)cluster.fork()

Подключения модулей

Легкость подключения модулей достигается двумя включениями.

Автоматическое подключение всех routes

// part of app.js
// путь к routes относительно запускаемого файла
var routesPath = path.join(__dirname, 'routes');

//лист всех файлов в директории
var routeList = fs.readdirSync(routesPath);
for(var i in routeList){
    var filePath = path.join(routesPath,routeList[i]);
    if(fs.statSync(filePath).isFile() && path.extname(routeList[i])=='.js')
        require(filePath)(app); // инициация путей
}

Подключение модуля и вызов функции

Routes дефинируются отдельно в каждом файле, чем так-же достигается унификация и простота подключения routes для конечного разработчика.

//файл api.js

module.exports = function (app) {
    app.get('/api/:mod/:lang/:action', function(req, res){action(req,res,global.conf.METHODS.GET);});
    app.post('/api/:mod/:lang/:action', function(req, res){action(req,res,global.conf.METHODS.POST);});
    app.delete('/api/:mod/:lang/:action', function(req, res){action(req,res,global.conf.METHODS.DELETE);});
    app.put('/api/:mod/:lang/:action', function(req, res){action(req,res,global.conf.METHODS.PUT);});
};


function action(req, res, method) {

    //проверка языка
    var lang = req.params.lang.toUpperCase();

    if (global.conf.AVAILABLE_LANGUAGES.indexOf(lang) > -1) {
      // чистка имени модуля 
	var mod = req.params.mod.replace(/[^a-zA-z0-9]+/g,'');
      // чистка имени функции
	var action = req.params.action.replace(/[^a-zA-z0-9]+/g,'');

      // проверка существования модуля  
	fs.exists(path.resolve(__dirname, './mod_api/'+mod+".js"),function(ok){
            if(ok){
		   // вызов модуля
                var startMode = require('./mod_api/'+mod);
                try{
		    // вызов функции с префиксом pub_
                    wait.launchFiber(startMode['pub_'+action], req, res, lang, method);
                }catch(err){
                    res.status(405).pj(405,err.message,"Method Not Allowed");
                }
            }else{
                res.status(503).pj(503,null,'Service Unavailable ');
            }
        });
    }
    else{
        res.status(400).pj(400,null,'Not supported language');
    }
}

Имплементация конечной функции

Все функции с префиксом pub_ являются публичными, все остальное приватное.

// file routes/mod_api/test.js
exports.pub_Start = function(req, res, lang, method){   
   res.pj(0,(method===global.conf.METHODS.POST)?"POST":"GET","SUCCESS ");
}

Унификация вывода

//part app.js
http.ServerResponse.prototype.pj = function(status,data,message){
    try {
        this.json({STATUS:status,CONTENT:data,MESSAGE:message});
    }catch(e) {
        console.error(e);
        this.json({STATUS:999,CONTENT:null,MESSAGE:'parse response error'});}
};

Ну и в завершении вызов функции: api.codevit.net/api/test/en/Start
(я очень надеюсь, что сервер не обвалиться, это мой тестовый сервер)

Если кому-то будет интересно, могу выложить скелет на github.

Поделиться

* - обязательные к заполнению поля