├── 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("
a new web framework based on NodeJS
Edward Zhang July 5, 2011