epi
Version:
A Api Service Framework Base On Express.js
512 lines (378 loc) • 11.9 kB
JavaScript
/**
* @desc 封装express创建服务的方法
* @author sam
* @version 0.8.3
* */
var https = require('https');
var cluster = require('cluster');
var os = require('os');
var util = require('util');
var METHODS = require('./methods').METHODS;
var EventEmitter = require('events').EventEmitter;
var express = require('express');
/**
* @desc 创建一个http服务程序,包括https和http服务
* @constructor HttpServer
* @this {HttpServer}
* @param {Object} options
* @example
*
*
*'use strict';
*
*var HttpServer = require('epi').HttpServer;
*
*var util = require('util');
*
* var config = {
* host : null,
* key : 'string', //https监听时需要这个key
* cert : 'string', //https监听的时候需要证书
* version : '1.0.0' //api默认的版本信息
* port : {
* http : 8000, //如果想要监听http端口,直接写
* https : 4433 //如果不想监听https,直接置为null
* }
* }
*
* //创建一个httpServer=====================================
* var httpServer = new HttpServer(config);
*
*
* //设置==================================================
* httpServer.setting(function (app) {
* app.set('trust proxy', true);
* app.set('x-powered-by', false);
* });
* //导入中间件=============================================
* var body = require('body-parser');
* var cookie = require('cookie-parser');
* var compression = require("compression");
*
* //应用中间件
* httpServer.use(function (app) {
* app.use(compression());
* app.use(cookie());
* app.use(body.json());
* app.use(body.urlencoded({
* extended: true
* }));
* });
*
*
* //导入路由==============================================
* var testRouter = require('./router/test');
*
* //注册路由
* testRouter.map(httpServer);
* //错误处理=============================================
* httpServer.error(function (err, req, res, next) {
* console.error(err.stack);
* res.status(500).json({
* error: err
* });
* });
* */
var HttpServer = function (options) {
/* *
* {
* host : null,
* key : 'string',
* cert : 'string',
* version : {
* defaults : '1.0.0',
* headers : {
* version : 'accept-version',
* channel : 'client-channel'
* }
* }, //api默认的版本信息
* port : {
* http : 8000,
* https : 4433
* }
* }
* */
options = options || {};
EventEmitter.call(this);
this.__host = options.host || null;
this.__version = options.version;
this.__key = options.key;
this.__cert = options.cert;
this.__port = {
http: options.port.http,
https: options.port.https
};
this.__version = {
defaults : (options.version && options.version.defaults) ? options.version.defaults : '1.0.0',
headers : (options.version && options.version.headers) ? options.version.headers : 'accept-version'
};
this.__channel = {
defaults : (options.channel && options.channel.defaults) ? options.channel.defaults : null,
headers : (options.channel && options.channel.headers) ? options.channel.headers : 'client-channel'
};
this.__cache = {
headers : {
request : (options.cache && options.cache.request) ? options.cache.request : 'use-api-cache',
response : (options.cache && options.cache.request) ? options.cache.request : 'use-api-cache'
}
};
this.__server = null;
/**
* 如果调用cluster方法启动程序,所有子进程都在这里
* */
this.workers = null;
/**
* express 创建的app,如果想调用express的相关方法,可以使用这个app
* */
this.app = null;
/**
* 导出express.Router 以支持sub app
* */
this.Router = express.Router;
this.__init();
};
util.inherits(HttpServer, EventEmitter);
/**
* @desc 初始化系统
* @access private
* */
HttpServer.prototype.__init = function () {
this.app = express();
var self = this;
if (this.__port.https) {
if (!this.__key || !this.__cert) {
throw new Error('listen https need key and cert');
}
this.__server = https.createServer({
key: this.__key,
cert: this.__cert
}, this.app);
}
/**
* 用于解析version和device的中间件
* 添加一个解析version和device设备类型的中间件
*/
this.app.use(function (req, res, next) {
req.startTime = Date.now();
req.version = req.get(self.__version.headers.version);
req.channel = req.get(self.__version.headers.channel);
req.useCache = req.get(self.__cache.headers.request) ? true : false;
//check api cache exits function
//if you want to use api cache by custom, rewrite this function
res.checkCache = function(fn){
fn(null, null);
};
next();
});
return this.app;
};
/**
* 挂接方法
* */
METHODS.forEach(function(method){
HttpServer.prototype[method] = function(path, options, handler, ttl){
var self = this;
if (!util.isObject(options)) {
throw new Error('options is object like {v : "1.0.0", c : "pc"}');
}
options = options || {};
options.v = options.v || self.__version;
/***
* @desc defined handler
* @param {Object} options example {v : '1.0.0', c : 'pc'}
* @param {function} handler function(req, res, next){}
* @param {Number} ttl the cache time In seconds
* */
self.app[method](path, function(req, res, next){
if (options.v && req.version !== options.v) {
return next();
}
if (options.c && req.channel !== options.c) {
return next();
}
if(util.isNumber(ttl) && req.useCache && util.isFunction(res.checkCache)){
// check if there are cache ,
// if cached then send the cached value to request client
res.checkCache(function(err, result){
if(!err && result){
//set a header to show the data come from apiCache
res.set(self.__cache.headers.response, 'true');
return res.send(result);
}else{
res.cacheTime = ttl; //cache time
handler(req, res, next);
}
});
}else if(!util.isFunction(res.checkCache)){
//tip user that , the res.checkCache function is rewrite with error
next(new Error('res.checkCache is must be a function, please check again'));
}else{
handler(req, res, next);
}
});
};
});
/**
* @desc 设置程序属性
* @param {Function} fn function(app){}
* @example
* httpServer.setting(function (app) {
* app.set('trust proxy', true);
* app.set('x-powered-by', false);
* });
* */
HttpServer.prototype.setting = function (fn) {
fn(this.app);
};
/**
* @desc 应用中间件
* @param fn {Function} function(app){}
* @example
* httpServer.use(function(app){
* app.use(compression());
* app.use(timeout('10s'));
* app.use(cookie());
* app.use(body.json());
* });
* */
HttpServer.prototype.use = function (fn) {
fn(this.app);
};
/**
* @desc 注册路由
* @param fn {Function} function(app){}
* */
HttpServer.prototype.route = function (fn) {
fn(this.app);
};
/**
* @desc 添加404处理
* @param fn {Function} function(req, res, next){}
* */
HttpServer.prototype.uriNotFoundError = function(fn){
this.app.all('*', fn);
};
/**
* @desc 添加错误处理
* @param fn {Function} function(err, req, res, next){}
* */
HttpServer.prototype.internalServerError = function (fn) {
this.app.use(fn);
};
/**
* @desc 监听端口
* @param callback {Function} 监听后返回的消息
* @example
* httpServer.listen(8000, function(err, message){
* console.log(message);
* })
* */
HttpServer.prototype.listen = function (callback) {
if (!this.__port.http && !this.__port.https) {
throw new Error('port.http or port.https is not set');
}
var listenMessage = '';
var flag = 0;
var count = 0;
var self = this;
if (this.__port.http) {
count++;
}
if (this.__port.https) {
count++;
}
if (this.__port.http) {
this.app.listen(this.__port.http, self.__host, function (err) {
flag++;
if (err) {
listenMessage += err.message;
}
var host = self.__host || '127.0.0.1';
listenMessage += 'server listen on : http://' +
host +
':' +
self.__port.http +
', pid : ' +
process.pid;
if (flag >= count) {
self.emit('listen', self.__port.http);
callback && callback(listenMessage);
}
});
}
if (this.__port.https) {
this.__server.listen(this.__port.https, self.__host, function (err) {
flag++;
if (err) {
listenMessage += err.message;
}
var host = self.__host || '127.0.0.1';
listenMessage += '\nserver listen on : https://' +
host +
':' +
self.__port.https +
', pid : ' +
process.pid;
if (flag >= count) {
self.emit('listen', self.__port.https);
callback && callback(listenMessage);
}
});
}
};
/**
* @desc 以集群的方式启动程序
* @param number {Number} 要启动的监听进程数量,null || 0 启动和 cpu个数 - 1 的进程数量
* @param callback {Function} 监听后返回的消息
* @example
* //子进程启动事件
* httpServer.on('childStart', function(worker){
* //记录子进程启动
* logger.info(Date.now(), worker.pid);
* });
*
*
* //子进程退出事件
* httpServer.on('childStop', function(worker, code, signal){
* //记录子进程非正常退出
* if(!signal && code !== 0){
* logger.fatal(Date.now(), worker.pid, code, signal);
* }
* });
*
* //子进程退出事件
* httpServer.on('childRestart', function(worker){
* //记录子进程重启
* logger.fatal(Date.now(), worker.pid);
* });
*
*
* //使用集群方式监听=======================================
* httpServer.cluster(0, function (message) {
* console.log(message);
* });
* */
HttpServer.prototype.cluster = function (number, callback) {
var self = this;
var cpuCount = number || os.cpus().length - 1;
self.workers = {};
if (cluster.isMaster) {
for (var i = 0; i < cpuCount; i++) {
var workerProcess = cluster.fork();
self.workers[workerProcess.process.pid] = workerProcess;
self.emit('childStart', workerProcess);
}
cluster.on('exit', function (worker, code, signal) {
self.emit('childStop', worker, code, signal);
delete self.workers[worker.process.pid];
setTimeout(function(){
var workProcess = cluster.fork();
self.workers[workProcess.process.pid] = cluster.fork();
self.emit('childRestart', workProcess);
}, 1000);
});
} else {
self.listen(callback);
}
};
exports.HttpServer = HttpServer;