Building a modular system based on Nodejs
Primary requirements
• ease of connecting modules
• standard URL structure
• multilingualism
• automatic change acceptance
• use of multiprocessor system capabilities
The system is based on Express . The wait.for module is used to facilitate writing code .
File structure
- root
- routes
- mod_api
- test.js
- api.js
- mod_api
- views
- public
- app.js
- Server.js
- routes
Linux server demonization
In our project, the forever module was used .
-w - allows changing modules without directly rebooting the server. Forever monitors changes and overloads the server as needed.
-l leads to the void, as it was decided that two logs is too much.
forever start -a -w -l /dev/null -o out.log -e err.log Server.js
Using the capabilities of a multiprocessor system
Launching multiple processes through the “cluster” module to distribute the load between the cores.
//файл 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()
Module connections
The ease of connecting modules is achieved by two switches.
Automatically connect all 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); // инициация путей
}
Module connection and function call
Routes are defined separately. in each file, how unification and ease of connecting routes for the end developer are also achieved.
//файл 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');
}
}
Implementation of the final function
All functions with the prefix pub_ are public, everything else is private.
// 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 ");
}
Output unification
//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'});}
};
Well, at the end, the function call: api.codevit.net/api/test/en/Start
(I really hope that the server does not crash, this is my test server)
If someone is interested, I can put the skeleton on github.
Please write about syntax errors in private.