├── Readme.md ├── log └── iframe.log ├── mng.md ├── pp.md ├── public └── hello.txt ├── run.sh ├── src ├── controller │ ├── cal.js │ ├── index.js │ └── post_test.js ├── lib │ ├── app_ctrl.js │ ├── child_mng.js │ ├── dispatch.js │ ├── env.js │ ├── log.js │ ├── misc.js │ └── route.js ├── master.js └── worker.js └── style.md /Readme.md: -------------------------------------------------------------------------------- 1 | ###IFrame 2 | 3 | A simple framework based on some real-project demands, it's very fast/elegant. 4 | 5 | It contains some components: 6 | 7 | ####Cluster 8 | 9 | the cluster writen by me ,please see : https://github.com/windyrobin/iCluster 10 | 11 | ####Controller 12 | 13 | A controller template which is very elegant and similiar to Rails, 14 | for example ,if you want to implement an add caculation : 15 | 16 | ``` 17 | http://.../cal/add?lnum=1&rnum=3 18 | ``` 19 | 20 | and you could write code like that : 21 | 22 | ``` 23 | require('../lib/env'); 24 | 25 | function Cal(){} 26 | 27 | exports.ctor = Cal; 28 | 29 | App.declare(Cal); 30 | 31 | Cal.actions = { 32 | index : true, 33 | add : true 34 | }; 35 | 36 | Cal.prototype.index = function(req, res){ 37 | this.add(req, res); 38 | } 39 | 40 | Cal.prototype.add = function(req, res){ 41 | //debug("add method"); 42 | var self = this; 43 | var params = self.query; 44 | var lnum = parseInt(params['lnum']); 45 | var rnum = parseInt(params['rnum']); 46 | var result = lnum + rnum 47 | res.writeHead(200, {'Content-Type': 'text/html'}); 48 | res.end('' + result); 49 | } 50 | ``` 51 | 52 | and it will return "4" to the client, 53 | 54 | for more details ,please see the files in `controller` directory. 55 | 56 | ####Router & Dispatcher 57 | 58 | the Router is very simple ,now only `/:controller/:action` mode supported ,you 59 | could rewrite it freely depend on your own 60 | 61 | ####Static File server 62 | 63 | static file service is supported 64 | 65 | 66 | ####Multi-process logging system 67 | 68 | It supports MAX log file size and it will rotate the log file automatically. 69 | only the master opereates the log file directly ,the worker process just sends log content to 70 | master via `process.send()` 71 | 72 | -------------------------------------------------------------------------------- /log/iframe.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyrobin/iFrame/83b8b8a0ef0aab5658c78605958856288dae74b1/log/iframe.log -------------------------------------------------------------------------------- /mng.md: -------------------------------------------------------------------------------- 1 | * 转变思想 2 | 3 | 抛弃传统的 Php/RoR/Django/Asp.net 的随意的写法,Node 中间层/高性能开发 4 | 是个特殊的领域,需要足够的严谨与细致 5 | 6 | * Node 中间层/高性能开发 = JavaScript + 系统编程 + 高性能服务器编程 7 | 8 | * 减少外部依赖,谨慎选择开源模块 9 | 10 | * 面向接口编程,不管内部实现如何,接口定义要简洁、清晰、固定,参数、 11 | 返回值的详细说明,是否会抛出异常,有何陷阱与缺陷 12 | 13 | * 对于复杂的逻辑运算,不仅要有unittest,还要有相应的benchmark test 14 | 15 | * 启用 Node 的 profile ,有时能够有助于你对算法瓶颈的查找、优化 16 | 17 | * 对于外部的网络请求,一定也要有相应的benchmark test ,摸清其 qps,response time , 18 | cpu 耗费 19 | 20 | * 选择使用 http keep-alive 21 | 22 | * 选择使用 tcp/http/mysql/memcache ... 等等连接池 23 | 24 | * 对于异步回调函数,一定要检查其是否错误,即第一个回调参数,并做相应处理 25 | 26 | * 复杂的逻辑运算函数,要加 try/catch 进行安全控制 27 | 28 | * 设置uncaughtException ,但你应该永远不让它有触发的机会 29 | 30 | * JS 不能解决的问题,如 算法性能,可以考虑尝试 C++ 扩展 -------------------------------------------------------------------------------- /pp.md: -------------------------------------------------------------------------------- 1 | # Node 陷阱与优化 2 | 3 | ## General JS 4 | 5 | * 遍历效率比较 6 | 7 | - forEach 很慢 8 | - for (var in in obj) 较慢 9 | - for (var i = 0; i < len; i++) 最快 10 | - Array 的遍历比 Object 要快的多 11 | 12 | * 避免重复计算 13 | 14 | Wrong: 15 | 16 | ```js 17 | for (var i = 0; i < arr.length; i++) { 18 | // ... 19 | } 20 | ``` 21 | 22 | 在 arr 长度较大时,应该引入临时量 (V8下此优化无效) 23 | 24 | Right: 25 | 26 | ```js 27 | var len = arr.length; 28 | for (var i = 0; i < len; i++) { 29 | // ... 30 | } 31 | 32 | // or 33 | 34 | for (var i = 0, l = arr.length; i < l; i++) { 35 | // ... 36 | } 37 | ``` 38 | 39 | * 使用临时变量,避免对象的多次查找 40 | 41 | Wrong: 42 | 43 | ```js 44 | var a = { 45 | b: { 46 | c: 'd' 47 | } 48 | }; 49 | for (;;) { 50 | if (a.b.c) { 51 | // ... 52 | } 53 | // ... 54 | } 55 | ``` 56 | 57 | Right: 58 | 59 | ```js 60 | var o = a.b.c; 61 | for (;;) { 62 | if (o) { 63 | // ... 64 | } 65 | // ... 66 | } 67 | ``` 68 | 69 | ## V8 JS 70 | 71 | * 使用 V8 支持的功能列表 [V8-ECMA](https://github.com/joyent/node/wiki/ECMA-5-Mozilla-Features-Implemented-in-V8) 72 | 如 : 73 | - ```Array.isArray``` 74 | - ```Array.indexOf``` 75 | - ```Object.keys``` 76 | - ```String.trim/trimLeft/trimRight``` 77 | - ... 78 | 79 | * 理解V8 对 ```Object``` 属性查找机制的改进,在构造函数中预先初始化所有属性 80 | 81 | * Try/Catch 位置 82 | 对较为复杂的函数运算,你应该在调用它时```try/catch``` ,这样效率更高 [V8_Catch](https://github.com/joyent/node/wiki/Best-practices-and-gotchas-with-v8) 83 | 84 | Wrong: 85 | 86 | ```js 87 | function tfn() { 88 | try { 89 | @#dsC23 90 | @##sd 91 | @#$d 92 | } catch (e) { 93 | // ... 94 | } 95 | } 96 | ``` 97 | 98 | Right: 99 | 100 | ```js 101 | try { 102 | tfn(); 103 | } catch (e) { 104 | // ... 105 | } 106 | ``` 107 | 108 | * 使用 WebGL 类型扩展: ```Int8Array```, ```Int16Array```, ```Float32Array``` ... 109 | 110 | 这些类型的数值运算、二进制运算非常快 (参见Node 自带的```benchamrk/array```); 111 | 112 | ## Node JS 113 | 114 | * Event 115 | 116 | Node 有两种Event: hard/soft, hard event 是指文件、网络可读写这些物理上的event, 117 | 其他的用户设置的事件都是 soft event;当你通过 ```obj.on('eid', function(){})``` 来添加事件时, 118 | 此 obj 内部会维护一个对应此 ```'eid'``` 的事件队列,所以,当你多次调用此函数时, 119 | 会把相同的处理函数设置多次。 120 | (Node 为了避免这种情况,设置了一个上限提示出错,用来避免内存泄露) 121 | 122 | 当程序调用 ```obj.emit('eid', data)``` ,不要被假象所迷惑,这会立即调用设置的回调函数, 123 | 它根本不是异步的。 124 | 125 | * Timer 126 | 127 | * 使用 ```process.nextTick(fun)``` 替代 ```setTimeout(fun, 0)``` 128 | * 可以的话,尽量 ```setTimeout(fun, timeout)``` 设置相同的超时值,timeout 值相同 129 | 时 Node 会使用同一个定时器处理 130 | 131 | ```js 132 | // code from `node/lib/timers.js` 133 | 134 | // IDLE TIMEOUTS 135 | // 136 | // Because often many sockets will have the same idle timeout we will not 137 | // use one timeout watcher per item. It is too much overhead. Instead 138 | // we'll use a single watcher for all sockets with the same timeout value 139 | // and a linked list. This technique is described in the libev manual: 140 | // http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts 141 | ``` 142 | 143 | * Buffer 144 | 145 | 避免不必要的拷贝以及与 ```string``` 的相互转换 146 | 比如你可能这样写: 147 | 148 | Wrong: 149 | 150 | ```js 151 | var chunks = [], nread = 0; 152 | stream.on('data', function (chunk) { 153 | chunks.push(chunk); 154 | nread += chunk.length; 155 | }); 156 | 157 | stream.on('end', function () { 158 | var buf = new Buffer(nread); 159 | for (var i = 0, l = chunks.length; i < l; i++) { 160 | chunks[i].copy(buf, ....); 161 | } 162 | }); 163 | ``` 164 | 165 | 但其实**很多时候**,仅仅**只有一个chunk**,完全可以避免多余的copy操作, 166 | 我们可以参看 Node 的源码实现: 167 | 168 | Right: (node >= 0.8 直接可以使用 [Buffer.concat](http://nodejs.org/docs/latest/api/buffer.html#buffer_class_method_buffer_concat_list_totallength)) 169 | 170 | ```js 171 | readStream.on('end', function () { 172 | // copy all the buffers into one 173 | var buf; 174 | switch (buffers.length) { 175 | case 0: buf = new Buffer(0); break; 176 | case 1: buf = buffers[0]; break; 177 | default: // concat together 178 | buffer = new Buffer(nread); 179 | for (var i = 0, p = 0, l = buffers.length; i < l; i++) { 180 | var b = buffers[i]; 181 | var size = b.length; 182 | b.copy(buf, p, 0 size); 183 | p += size; 184 | } 185 | break; 186 | } 187 | // ... use buf ... 188 | ``` 189 | 190 | * File System 191 | 192 | * 在读取文件时,可以的话,尽量传入适当的```bufferSize``` 193 | * 谨慎使用**同步**操作函数组 - 除非你清楚知道这样带来的后果 194 | 195 | * Stream 196 | 197 | * ```Stream```类的抽象是 Node 的亮点之一,也是一个非常重要的基础类,你应该深刻的了解它 198 | * 不要持久引用 ```stream('on', data)``` 上浮的 ```Buffer``` 199 | 200 | * Net/Http Request 201 | 202 | 你应当给 ```socket/request``` 加上超时控制 203 | 204 | * Http Agent 205 | 206 | 从 ```Node V0.5.3``` 开始,Node 提供了这种方式来支持 ```keep-alive``` 连接池 207 | 208 | ```js 209 | // code from `node/lib/http.js` 210 | 211 | } else if (self.agent) { 212 | // If there is an agent we should default to Connection:keep-alive. 213 | self._last = false; 214 | self.shouldKeepAlive = true; 215 | self.agent.addRequest(self, options.host, options.port); 216 | } else { 217 | // No agent, default to Connection:close. 218 | self._last = true; 219 | self.shouldKeepAlive = false; 220 | ``` 221 | 222 | 但需要注意它的文档说明 223 | 224 | >If no pending HTTP requests are waiting on a socket to become free the socket is closed. 225 | 226 | 如果当前有pending 的request ,此时空闲状态的socket 会立即重用,如果没有,就会立即关闭: 227 | 228 | ```js 229 | // code from `node/lib/http.js` 230 | 231 | self.on('free', function(socket, host, port) { 232 | var name = host + ':' + port; 233 | if (self.requests[name] && self.requests[name].length) { 234 | self.requests[name].shift().onSocket(socket); 235 | } else { 236 | // If there are no pending requests just destroy the 237 | // socket and it will get removed from the pool. This 238 | // gets us out of timeout issues and allows us to 239 | // default to Connection:keep-alive. 240 | socket.destroy(); 241 | } 242 | }); 243 | 244 | // ... 245 | 246 | Agent.prototype.addRequest = function(req, host, port) { 247 | var name = host + ':' + port; 248 | if (!this.sockets[name]) { 249 | this.sockets[name] = []; 250 | } 251 | if (this.sockets[name].length < this.maxSockets) { 252 | // If we are under maxSockets create a new one. 253 | req.onSocket(this.createSocket(name, host, port)); 254 | } else { 255 | // We are over limit so we'll add it to the queue. 256 | if (!this.requests[name]) { 257 | this.requests[name] = []; 258 | } 259 | this.requests[name].push(req); 260 | } 261 | }; 262 | ``` 263 | 264 | 也就是说它没有我们传统意义上的keep-alive time的设置,比如当你在一个请求返回之后再请求 265 | 下一个,此时这个连接池是没有启用的. 266 | 267 | 以下是测试代码,每次请求的socket端口号都在变化,代表请求socket并没有被复用. 268 | 269 | Server 270 | 271 | ```js 272 | var http = require('http'); 273 | http.createServer(function (req, res) { 274 | // remotePort change, keep-alive not success. 275 | console.log('%d port', req.socket.remotePort); 276 | res.end('hello'); 277 | }).listen(3458); 278 | ``` 279 | 280 | Client 281 | 282 | ```js 283 | var http = require('http'); 284 | 285 | var count = 10; 286 | var agent = new http.Agent({ maxSockets: 3 }); 287 | var options = { 288 | host: 'localhost', 289 | port: 3458, 290 | path: '/', 291 | method: 'GET', 292 | agent: agent, 293 | headers: { Connection: 'keep-alive' } 294 | }; 295 | function looptest() { 296 | var req = http.request(options, function (res) { 297 | res.on('end', function () { 298 | console.log('count : %d', count); 299 | if (--count > 0) { 300 | process.nextTick(function () { 301 | looptest(); 302 | }); 303 | } 304 | }); 305 | }); 306 | req.end(); 307 | } 308 | 309 | looptest(); 310 | ``` 311 | 312 | * Http Response 313 | 314 | * 不要多次调用 ```res.write```,这会极大的影响性能,最好仅调用一次 315 | ```res.end(buf/string)``` 方法 316 | * 尽量在 ```res.writeHead``` 时设置 ```Content-Length``` 317 | -------------------------------------------------------------------------------- /public/hello.txt: -------------------------------------------------------------------------------- 1 | hello,world 2 | static files here 3 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | node ./src/master.js 2 | -------------------------------------------------------------------------------- /src/controller/cal.js: -------------------------------------------------------------------------------- 1 | require('../lib/env'); 2 | 3 | function Cal(){} 4 | 5 | exports.ctor = Cal; 6 | 7 | App.declare(Cal); 8 | 9 | Cal.actions = { 10 | index : true, 11 | add : true, 12 | sub : true 13 | }; 14 | 15 | Cal.prototype.index = function(req, res){ 16 | this.add(req, res); 17 | } 18 | 19 | Cal.prototype.add = function(req, res){ 20 | //debug('add method'); 21 | var self = this; 22 | var params = self.query; 23 | var lnum = parseInt(params['lnum']); 24 | var rnum = parseInt(params['rnum']); 25 | var result = lnum + rnum 26 | res.writeHead(200, {'Content-Type': 'text/html'}); 27 | res.end('' + result); 28 | } 29 | 30 | Cal.prototype.sub = function(req, res){ 31 | var self = this; 32 | var params = self.query; 33 | var lnum = parseInt(params['lnum']); 34 | var rnum = parseInt(params['rnum']); 35 | var result = lnum - rnum; 36 | res.writeHead(200, {'Content-Type': 'text/html'}); 37 | res.end('' + result); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/controller/index.js: -------------------------------------------------------------------------------- 1 | require('../lib/env'); 2 | 3 | function Index(){} 4 | 5 | exports.ctor = Index; 6 | 7 | App.declare(Index); 8 | 9 | Index.prototype.index = function(req, res){ 10 | res.writeHead(200, {'Content-Type' : 'text/html'}); 11 | res.end("

Welcome



a new web framework based on NodeJS

Edward Zhang July 5, 2011

"); 12 | } 13 | -------------------------------------------------------------------------------- /src/controller/post_test.js: -------------------------------------------------------------------------------- 1 | require('../lib/env'); 2 | 3 | function PostTest(){} 4 | 5 | exports.ctor = PostTest; 6 | 7 | App.declare(PossTest); 8 | 9 | PostTest.prototype.index = function(req, res){ 10 | var self = this; 11 | res.writeHead(200, {'Content-Type' : 'text/plain' ,'connection' : 'keep-alive'}); 12 | res.write(self.body); 13 | res.end(''); 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/app_ctrl.js: -------------------------------------------------------------------------------- 1 | require('./env'); 2 | 3 | //exports.ctor = AppCtrl; 4 | exports.create = ctrlCreate; 5 | exports.declare = ctrlDeclare; 6 | 7 | function AppCtrl(req, res){ 8 | this.path = ''; 9 | this.query = {}; 10 | //if the request method is POST ,we will set it in 11 | //process function 12 | } 13 | 14 | AppCtrl.prototype.run = function(meta, req, res){ 15 | //get the sub-controller(method) 16 | var action = meta['action']; 17 | var func = this[action]; 18 | 19 | this.path = meta.path; 20 | this.query = meta.query; 21 | 22 | var self = this; 23 | //maybe we should add file upload support in future 24 | 25 | if(req.method == 'POST'){ 26 | self.body = []; 27 | req.on('data', function(chuck){ 28 | self.body.push(chuck.toString()); 29 | }); 30 | } 31 | req.on('end',function(){ 32 | if(self.body){ 33 | self.body = self.body.join(''); 34 | } 35 | try{ 36 | func.call(self, req, res); 37 | }catch(ex){ 38 | log.error(ex.toString()); 39 | replyError(req, res, 500); 40 | } 41 | }); 42 | } 43 | 44 | AppCtrl.prototype.preFilter = function(){ 45 | } 46 | 47 | function ctrlCreate(Ctrl){ 48 | var inst = new Ctrl(); 49 | AppCtrl.call(inst); 50 | return inst; 51 | } 52 | 53 | function ctrlDeclare(Ctrl){ 54 | util.inherits(Ctrl, AppCtrl); 55 | } 56 | -------------------------------------------------------------------------------- /src/lib/child_mng.js: -------------------------------------------------------------------------------- 1 | require('./env'); 2 | 3 | var childs = []; 4 | var childStatus = []; 5 | 6 | exports.push = function(c){ 7 | childs.push(c); 8 | c.on('message', msgCtrlFactory(childs.length - 1)); 9 | } 10 | 11 | exports.getStatus = function(){ 12 | return childStatus; 13 | } 14 | 15 | exports.kill = function(pos){ 16 | pos = pos || -1; 17 | pos = parseInt(pos); 18 | if(pos < 0){ 19 | childs.forEach(function(c){ 20 | process.kill(c.pid); 21 | }); 22 | }else if(pos > 0 && pos < childs.length){ 23 | process.kill(childs[pos].pid); 24 | } 25 | } 26 | 27 | exports.updateStatus = function(){ 28 | childs.forEach(function(c){ 29 | c.send({status : 'update'}); 30 | }) 31 | } 32 | 33 | function simple_hash(str){ 34 | var sum = 0; 35 | for(var i=0; i= MAX_SIZE){ 31 | exports.destroy(); 32 | try{ 33 | fs.unlinkSync(LOG_FILE_OLD); 34 | debug('delete old log'); 35 | }catch(e){ 36 | } 37 | fs.renameSync(LOG_FILE ,LOG_FILE_OLD); 38 | debug('rename ok'); 39 | exports.init(); 40 | } 41 | }); 42 | }, CHECK_INTERVAL); 43 | debug('master log init ok'); 44 | } 45 | }, 46 | 47 | exports.destroy = function(){ 48 | if(checkEvent != null){ 49 | clearInterval(checkEvent); 50 | checkEvent = null; 51 | } 52 | if(logStream){ 53 | logStream.end(); 54 | logStream.destroySoon(); 55 | } 56 | } 57 | 58 | exports.write = function(str){ 59 | logWrite(str); 60 | } 61 | exports.error = function(str){ 62 | logWrite('[Error]\t' + str + '\t'+ new Date()); 63 | }, 64 | 65 | exports.warning = function(str){ 66 | logWrite('[Warning]\t' + str + '\t' + new Date()); 67 | } 68 | 69 | exports.info = function(str){ 70 | logWrite('[Info]\t' + str + '\t' + new Date()); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/lib/misc.js: -------------------------------------------------------------------------------- 1 | // List of most common mime-types, stolen from Rack. 2 | exports.getType = function(type){ 3 | const DEFAULT_CONTENT_TYPE = 'application/octet-stream'; 4 | var mime = { '.3gp' : 'video/3gpp' 5 | , '.a' : 'application/octet-stream' 6 | , '.ai' : 'application/postscript' 7 | , '.aif' : 'audio/x-aiff' 8 | , '.aiff' : 'audio/x-aiff' 9 | , '.asc' : 'application/pgp-signature' 10 | , '.asf' : 'video/x-ms-asf' 11 | , '.asm' : 'text/x-asm' 12 | , '.asx' : 'video/x-ms-asf' 13 | , '.atom' : 'application/atom+xml' 14 | , '.au' : 'audio/basic' 15 | , '.avi' : 'video/x-msvideo' 16 | , '.bat' : 'application/x-msdownload' 17 | , '.bin' : 'application/octet-stream' 18 | , '.bmp' : 'image/bmp' 19 | , '.bz2' : 'application/x-bzip2' 20 | , '.c' : 'text/x-c' 21 | , '.cab' : 'application/vnd.ms-cab-compressed' 22 | , '.cc' : 'text/x-c' 23 | , '.chm' : 'application/vnd.ms-htmlhelp' 24 | , '.class' : 'application/octet-stream' 25 | , '.com' : 'application/x-msdownload' 26 | , '.conf' : 'text/plain' 27 | , '.cpp' : 'text/x-c' 28 | , '.crt' : 'application/x-x509-ca-cert' 29 | , '.css' : 'text/css' 30 | , '.csv' : 'text/csv' 31 | , '.cxx' : 'text/x-c' 32 | , '.deb' : 'application/x-debian-package' 33 | , '.der' : 'application/x-x509-ca-cert' 34 | , '.diff' : 'text/x-diff' 35 | , '.djv' : 'image/vnd.djvu' 36 | , '.djvu' : 'image/vnd.djvu' 37 | , '.dll' : 'application/x-msdownload' 38 | , '.dmg' : 'application/octet-stream' 39 | , '.doc' : 'application/msword' 40 | , '.dot' : 'application/msword' 41 | , '.dtd' : 'application/xml-dtd' 42 | , '.dvi' : 'application/x-dvi' 43 | , '.ear' : 'application/java-archive' 44 | , '.eml' : 'message/rfc822' 45 | , '.eps' : 'application/postscript' 46 | , '.exe' : 'application/x-msdownload' 47 | , '.f' : 'text/x-fortran' 48 | , '.f77' : 'text/x-fortran' 49 | , '.f90' : 'text/x-fortran' 50 | , '.flv' : 'video/x-flv' 51 | , '.for' : 'text/x-fortran' 52 | , '.gem' : 'application/octet-stream' 53 | , '.gemspec' : 'text/x-script.ruby' 54 | , '.gif' : 'image/gif' 55 | , '.gz' : 'application/x-gzip' 56 | , '.h' : 'text/x-c' 57 | , '.hh' : 'text/x-c' 58 | , '.htm' : 'text/html' 59 | , '.html' : 'text/html' 60 | , '.ico' : 'image/vnd.microsoft.icon' 61 | , '.ics' : 'text/calendar' 62 | , '.ifb' : 'text/calendar' 63 | , '.iso' : 'application/octet-stream' 64 | , '.jar' : 'application/java-archive' 65 | , '.java' : 'text/x-java-source' 66 | , '.jnlp' : 'application/x-java-jnlp-file' 67 | , '.jpeg' : 'image/jpeg' 68 | , '.jpg' : 'image/jpeg' 69 | , '.js' : 'application/javascript' 70 | , '.json' : 'application/json' 71 | , '.log' : 'text/plain' 72 | , '.m3u' : 'audio/x-mpegurl' 73 | , '.m4v' : 'video/mp4' 74 | , '.man' : 'text/troff' 75 | , '.mathml' : 'application/mathml+xml' 76 | , '.mbox' : 'application/mbox' 77 | , '.mdoc' : 'text/troff' 78 | , '.me' : 'text/troff' 79 | , '.mid' : 'audio/midi' 80 | , '.midi' : 'audio/midi' 81 | , '.mime' : 'message/rfc822' 82 | , '.mml' : 'application/mathml+xml' 83 | , '.mng' : 'video/x-mng' 84 | , '.mov' : 'video/quicktime' 85 | , '.mp3' : 'audio/mpeg' 86 | , '.mp4' : 'video/mp4' 87 | , '.mp4v' : 'video/mp4' 88 | , '.mpeg' : 'video/mpeg' 89 | , '.mpg' : 'video/mpeg' 90 | , '.ms' : 'text/troff' 91 | , '.msi' : 'application/x-msdownload' 92 | , '.odp' : 'application/vnd.oasis.opendocument.presentation' 93 | , '.ods' : 'application/vnd.oasis.opendocument.spreadsheet' 94 | , '.odt' : 'application/vnd.oasis.opendocument.text' 95 | , '.ogg' : 'application/ogg' 96 | , '.p' : 'text/x-pascal' 97 | , '.pas' : 'text/x-pascal' 98 | , '.pbm' : 'image/x-portable-bitmap' 99 | , '.pdf' : 'application/pdf' 100 | , '.pem' : 'application/x-x509-ca-cert' 101 | , '.pgm' : 'image/x-portable-graymap' 102 | , '.pgp' : 'application/pgp-encrypted' 103 | , '.pkg' : 'application/octet-stream' 104 | , '.pl' : 'text/x-script.perl' 105 | , '.pm' : 'text/x-script.perl-module' 106 | , '.png' : 'image/png' 107 | , '.pnm' : 'image/x-portable-anymap' 108 | , '.ppm' : 'image/x-portable-pixmap' 109 | , '.pps' : 'application/vnd.ms-powerpoint' 110 | , '.ppt' : 'application/vnd.ms-powerpoint' 111 | , '.ps' : 'application/postscript' 112 | , '.psd' : 'image/vnd.adobe.photoshop' 113 | , '.py' : 'text/x-script.python' 114 | , '.qt' : 'video/quicktime' 115 | , '.ra' : 'audio/x-pn-realaudio' 116 | , '.rake' : 'text/x-script.ruby' 117 | , '.ram' : 'audio/x-pn-realaudio' 118 | , '.rar' : 'application/x-rar-compressed' 119 | , '.rb' : 'text/x-script.ruby' 120 | , '.rdf' : 'application/rdf+xml' 121 | , '.roff' : 'text/troff' 122 | , '.rpm' : 'application/x-redhat-package-manager' 123 | , '.rss' : 'application/rss+xml' 124 | , '.rtf' : 'application/rtf' 125 | , '.ru' : 'text/x-script.ruby' 126 | , '.s' : 'text/x-asm' 127 | , '.sgm' : 'text/sgml' 128 | , '.sgml' : 'text/sgml' 129 | , '.sh' : 'application/x-sh' 130 | , '.sig' : 'application/pgp-signature' 131 | , '.snd' : 'audio/basic' 132 | , '.so' : 'application/octet-stream' 133 | , '.svg' : 'image/svg+xml' 134 | , '.svgz' : 'image/svg+xml' 135 | , '.swf' : 'application/x-shockwave-flash' 136 | , '.t' : 'text/troff' 137 | , '.tar' : 'application/x-tar' 138 | , '.tbz' : 'application/x-bzip-compressed-tar' 139 | , '.tcl' : 'application/x-tcl' 140 | , '.tex' : 'application/x-tex' 141 | , '.texi' : 'application/x-texinfo' 142 | , '.texinfo' : 'application/x-texinfo' 143 | , '.text' : 'text/plain' 144 | , '.tif' : 'image/tiff' 145 | , '.tiff' : 'image/tiff' 146 | , '.torrent' : 'application/x-bittorrent' 147 | , '.tr' : 'text/troff' 148 | , '.txt' : 'text/plain' 149 | , '.vcf' : 'text/x-vcard' 150 | , '.vcs' : 'text/x-vcalendar' 151 | , '.vrml' : 'model/vrml' 152 | , '.war' : 'application/java-archive' 153 | , '.wav' : 'audio/x-wav' 154 | , '.wma' : 'audio/x-ms-wma' 155 | , '.wmv' : 'video/x-ms-wmv' 156 | , '.wmx' : 'video/x-ms-wmx' 157 | , '.wrl' : 'model/vrml' 158 | , '.wsdl' : 'application/wsdl+xml' 159 | , '.xbm' : 'image/x-xbitmap' 160 | , '.xhtml' : 'application/xhtml+xml' 161 | , '.xls' : 'application/vnd.ms-excel' 162 | , '.xml' : 'application/xml' 163 | , '.xpm' : 'image/x-xpixmap' 164 | , '.xsl' : 'application/xml' 165 | , '.xslt' : 'application/xslt+xml' 166 | , '.yaml' : 'text/yaml' 167 | , '.yml' : 'text/yaml' 168 | , '.zip' : 'application/zip' 169 | }; 170 | 171 | var type = mime[type]; 172 | if(type == null) type = DEFAULT_CONTENT_TYPE; 173 | return type; 174 | } 175 | -------------------------------------------------------------------------------- /src/lib/route.js: -------------------------------------------------------------------------------- 1 | require('./env'); 2 | 3 | exports.fn = route; 4 | 5 | /*give a url ,and it will return meta object which 6 | **indict the controller ,the sub method ,and also the 7 | *parsed query string map ,and the request path 8 | */ 9 | function route(url){ 10 | var meta = { 11 | ctrl : 'index', 12 | action : 'index', 13 | query : null, 14 | path : '' 15 | }; 16 | 17 | var purl = require('url').parse(url, true); 18 | var arr = purl.pathname.split('/'); 19 | //the pathname always startd with '/' ,for example '/index/helo' 20 | //so arr[0] is always '' 21 | //console.log(util.inspect(arr)); 22 | 23 | if(arr[1] && arr[1] != '') 24 | meta.ctrl = arr[1]; 25 | if(arr[2] && arr[2] != '') 26 | meta.action = arr[2]; 27 | 28 | //parsed usr object ,avoid parsing it again 29 | meta.query = purl.query; 30 | meta.path = purl.pathname; 31 | return meta; 32 | } 33 | 34 | /* 35 | function debug(obj){ 36 | console.log(util.inspect(obj)); 37 | } 38 | debug(route('/hello?id=3')); 39 | debug(route('/')); 40 | debug(route('/hello/world?id=3')); 41 | */ 42 | -------------------------------------------------------------------------------- /src/master.js: -------------------------------------------------------------------------------- 1 | //var http = require('http'); 2 | require('./lib/env'); 3 | var cp = require('child_process'); 4 | var childMng = require('./lib/child_mng'); 5 | 6 | var PORT = 3459; 7 | var WORKER_NUMBER = 5; 8 | var GRACE_EXIT_TIME = 2000;//2s 9 | var WORKER_PATH = __dirname + '/worker.js'; 10 | var WORKER_HEART_BEAT = 10*1000;//10s, update memory ,etc 11 | 12 | function startWorker(handle){ 13 | output('start workers :' + WORKER_NUMBER); 14 | for(var i=0; i