Основные требования
• легкость подключения модулей
• стандартная структура URL
• многоязычность
• автоматическое принятие изменений
• использование возможностей многопроцессорной системы
Система построена на базе Express. Для облегчения написания кода используется модуль wait.for
Файловая структура
- root
- routes
- mod_api
- test.js
- api.js
- mod_api
- views
- public
- app.js
- Server.js
- routes
Демонизация сервера под линукс
В нашем проекте был использован модуль 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.
О синтаксических ошибках прошу писать в приват.
