七天学会NodeJS——第一天

前端开发 作者: 2024-08-22 02:20:01
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。原文出处:http://nqdeng.github.io/7-days-nodejs Node.js 是一个能

什么是NodeJS

有啥用处

如何安装

安装程序

编译安装

  1.  $ ./configure
     $ make
     $ sudo make install

如何运行

$ node
> console.log('Hello World!');
Hello World!
function hello() {
    console.log('Hello World!');
}
hello();
$ node hello.js
Hello World!

权限问题

$ sudo node server.js
$ sudo chown root /usr/local/bin/node
$ sudo chmod +s /usr/local/bin/node

模块

require

var foo1 = require('./foo');
var foo2 = require('./foo.js');
var foo3 = require('/home/user/foo');
var foo4 = require('/home/user/foo.js');

// foo1至foo4中保存的是同一个模块的导出对象。
var data = require('./data.json');

exports

exports.hello = function () {
    console.log('Hello World!');
};

module

module.exports = function () {
    console.log('Hello World!');
};

模块初始化

主模块

$ node main.js

完整示例

- /home/user/hello/
    - util/
        counter.js
    main.js
var i = 0;

function count() {
    return ++i;
}

exports.count = count;
var counter1 = require('./util/counter');
var    counter2 = require('./util/counter');

console.log(counter1.count());
console.log(counter2.count());
console.log(counter2.count());
$ node main.js
1
2
3

二进制模块

小结

模块路径解析规则

  1.  /home/user/node_modules/foo/bar
     /home/node_modules/foo/bar
     /node_modules/foo/bar
  2.  NODE_PATH=/home/user/lib:/home/lib
     /home/user/lib/foo/bar
     /home/lib/foo/bar

包(package)

- /home/user/lib/
    - cat/
        head.js
        body.js
        main.js
var head = require('./head');
var body = require('./body');

exports.create = function (name) {
    return {
        name: name,head: head.create(),body: body.create()
    };
};

index.js

var cat = require('/home/user/lib/cat');
var cat = require('/home/user/lib/cat/index');

package.json

- /home/user/lib/
    - cat/
        + doc/
        - lib/
            head.js
            body.js
            main.js
        + tests/
        package.json
{
    "name": "cat","main": "./lib/main.js"
}

命令行程序

$ node /home/user/bin/node-echo.js Hello World
Hello World
$ node-echo Hello World

Linux

  1.  #! /usr/bin/env node
  2.  $ chmod +x /home/user/bin/node-echo.js
  3.  $ sudo ln -s /home/user/bin/node-echo.js /usr/local/bin/node-echo

Windows

@node "C:\User\user\bin\node-echo.js" %*

工程目录

- /home/user/workspace/node-echo/   # 工程目录
    - bin/                          # 存放命令行相关代码
        node-echo
    + doc/                          # 存放文档
    - lib/                          # 存放API相关代码
        echo.js
    - node_modules/                 # 存放三方包
        + argv/
    + tests/                        # 存放测试用例
    package.json                    # 元数据文件
    README.md                       # 说明文件
/* bin/node-echo */
var argv = require('argv'),echo = require('../lib/echo');
console.log(echo(argv.join(' ')));

/* lib/echo.js */
module.exports = function (message) {
    return message;
};

/* package.json */
{
    "name": "node-echo","main": "./lib/echo.js"
}

NPM

下载三方包

$ npm install argv
...
argv@0.0.2 node_modules\argv
$ npm install argv@0.0.1
...
argv@0.0.1 node_modules\argv
{
    "name": "node-echo","main": "./lib/echo.js","dependencies": {
        "argv": "0.0.2"
    }
}
- project/
    - node_modules/
        - node-echo/
            - node_modules/
                + argv/
            ...
    ...

安装命令行程序

$ npm install node-echo -g
- /usr/local/               # Linux系统下
    - lib/node_modules/
        + node-echo/
        ...
    - bin/
        node-echo
        ...
    ...

- %APPDATA%\npm\            # Windows系统下
    - node_modules\
        + node-echo\
        ...
    node-echo.cmd
    ...

发布代码

{
    "name": "node-echo",# 包名,在NPM服务器上须要保持唯一
    "version": "1.0.0",# 当前版本号
    "dependencies": {              # 三方包依赖,需要指定包名和版本号
        "argv": "0.0.2"
      },# 入口模块位置
    "bin" : {
        "node-echo": "./bin/node-echo"      # 命令行程序名和主模块位置
    }
}

版本号

+ 如果只是修复bug,需要更新Z位。

+ 如果是新增了功能,但是向下兼容,需要更新Y位。

+ 如果有大变动,向下不兼容,需要更新X位。

灵机一点

小结

开门红

小文件拷贝

var fs = require('fs');

function copy(src,dst) {
    fs.writeFileSync(dst,fs.readFileSync(src));
}

function main(argv) {
    copy(argv[0],argv[1]);
}

main(process.argv.slice(2));

大文件拷贝

var fs = require('fs');

function copy(src,dst) {
    fs.createReadStream(src).pipe(fs.createWriteStream(dst));
}

function main(argv) {
    copy(argv[0],argv[1]);
}

main(process.argv.slice(2));

API走马观花

Buffer(数据块)

var bin = new Buffer([ 0x68,0x65,0x6c,0x6f ]);
bin[0]; // => 0x68;
var str = bin.toString('utf-8'); // => "hello"
var bin = new Buffer('hello','utf-8'); // => <Buffer 68 65 6c 6c 6f>
bin[0] = 0x48;
[ 0x68,0x6f ]
    ^           ^
    |           |
   bin     bin.slice(2)
var bin = new Buffer([ 0x68,0x6f ]);
var sub = bin.slice(2);

sub[0] = 0x65;
console.log(bin); // => <Buffer 68 65 65 6c 6f>
var bin = new Buffer([ 0x68,0x6f ]);
var dup = new Buffer(bin.length);

bin.copy(dup);
dup[0] = 0x48;
console.log(bin); // => <Buffer 68 65 6c 6c 6f>
console.log(dup); // => <Buffer 48 65 65 6c 6f>

Stream(数据流)

var rs = fs.createReadStream(pathname);

rs.on('data',function (chunk) {
    doSomething(chunk);
});

rs.on('end',function () {
    cleanUp();
});
var rs = fs.createReadStream(src);

rs.on('data',function (chunk) {
    rs.pause();
    doSomething(chunk,function () {
        rs.resume();
    });
});

rs.on('end',function () {
    cleanUp();
});
var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);

rs.on('data',function (chunk) {
    ws.write(chunk);
});

rs.on('end',function () {
    ws.end();
});
var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);

rs.on('data',function (chunk) {
    if (ws.write(chunk) === false) {
        rs.pause();
    }
});

rs.on('end',function () {
    ws.end();
});

ws.on('drain',function () {
    rs.resume();
});

File System(文件系统)

fs.readFile(pathname,function (err,data) {
    if (err) {
        // Deal with error.
    } else {
        // Deal with data.
    }
});
try {
    var data = fs.readFileSync(pathname);
    // Deal with data.
} catch (err) {
    // Deal with error.
}

Path(路径)

  •   var cache = {};
    
      function store(key,value) {
          cache[path.normalize(key)] = value;
      }
    
      store('foo/bar',1);
      store('foo//baz//../bar',2);
      console.log(cache);  // => { "foo/bar": 2 }
  •   path.join('foo/','baz/','../bar'); // => "foo/bar"
  •   path.extname('foo/bar.js'); // => ".js"

遍历目录

递归算法

function factorial(n) {
    if (n === 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

遍历算法

          A
         / \
        B   C
       / \   \
      D   E   F

同步遍历

function travel(dir,callback) {
    fs.readdirSync(dir).forEach(function (file) {
        var pathname = path.join(dir,file);

        if (fs.statSync(pathname).isDirectory()) {
            travel(pathname,callback);
        } else {
            callback(pathname);
        }
    });
}
- /home/user/
    - foo/
        x.js
    - bar/
        y.js
    z.css
travel('/home/user',function (pathname) {
    console.log(pathname);
});

------------------------
/home/user/foo/x.js
/home/user/bar/y.js
/home/user/z.css

异步遍历

function travel(dir,callback,finish) {
    fs.readdir(dir,files) {
        (function next(i) {
            if (i < files.length) {
                var pathname = path.join(dir,files[i]);

                fs.stat(pathname,stats) {
                    if (stats.isDirectory()) {
                        travel(pathname,function () {
                            next(i + 1);
                        });
                    } else {
                        callback(pathname,function () {
                            next(i + 1);
                        });
                    }
                });
            } else {
                finish && finish();
            }
        }(0));
    });
}

文本编码

BOM的移除

    Bytes      Encoding
----------------------------
    FE FF       UTF16BE
    FF FE       UTF16LE
    EF BB BF    UTF8
function readText(pathname) {
    var bin = fs.readFileSync(pathname);

    if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) {
        bin = bin.slice(3);
    }

    return bin.toString('utf-8');
}

GBK转UTF8

var iconv = require('iconv-lite');

function readGBKText(pathname) {
    var bin = fs.readFileSync(pathname);

    return iconv.decode(bin,'gbk');
}

单字节编码

1. GBK编码源文件内容:
    var foo = '中文';
2. 对应字节:
    76 61 72 20 66 6F 6F 20 3D 20 27 D6 D0 CE C4 27 3B
3. 使用单字节编码读取后得到的内容:
    var foo = '{乱码}{乱码}{乱码}{乱码}';
4. 替换内容:
    var bar = '{乱码}{乱码}{乱码}{乱码}';
5. 使用单字节编码保存后对应字节:
    76 61 72 20 62 61 72 20 3D 20 27 D6 D0 CE C4 27 3B
6. 使用GBK编码读取后得到内容:
    var bar = '中文';
function replace(pathname) {
    var str = fs.readFileSync(pathname,'binary');
    str = str.replace('foo','bar');
    fs.writeFileSync(pathname,str,'binary');
}

小结

开门红

var http = require('http');

http.createServer(function (request,response) {
    response.writeHead(200,{ 'Content-Type': 'text-plain' });
    response.end('Hello World\n');
}).listen(8124);

API走马观花

HTTP

POST / HTTP/1.1
User-Agent: curl/7.26.0
Host: localhost
Accept: */*
Content-Length: 11
Content-Type: application/x-www-form-urlencoded

Hello World
http.createServer(function (request,response) {
    var body = [];

    console.log(request.method);
    console.log(request.headers);

    request.on('data',function (chunk) {
        body.push(chunk);
    });

    request.on('end',function () {
        body = Buffer.concat(body);
        console.log(body.toString());
    });
}).listen(80);

------------------------------------
POST
{ 'user-agent': 'curl/7.26.0',host: 'localhost',accept: '*/*','content-length': '11','content-type': 'application/x-www-form-urlencoded' }
Hello World
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 11
Date: Tue,05 Nov 2013 05:31:38 GMT
Connection: keep-alive

Hello World
http.createServer(function (request,{ 'Content-Type': 'text/plain' });

    request.on('data',function (chunk) {
        response.write(chunk);
    });

    request.on('end',function () {
        response.end();
    });
}).listen(80);
var options = {
        hostname: 'www.example.com',port: 80,path: '/upload',method: 'POST',headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };

var request = http.request(options,function (response) {});

request.write('Hello World');
request.end();
http.get('http://www.example.com/',function (response) {});
http.get('http://www.example.com/',function (response) {
    var body = [];

    console.log(response.statusCode);
    console.log(response.headers);

    response.on('data',function (chunk) {
        body.push(chunk);
    });

    response.on('end',function () {
        body = Buffer.concat(body);
        console.log(body.toString());
    });
});

------------------------------------
200
{ 'content-type': 'text/html',server: 'Apache','content-length': '801',date: 'Tue,05 Nov 2013 06:08:41 GMT',connection: 'keep-alive' }
<!DOCTYPE html>
...

HTTPS

var options = {
        key: fs.readFileSync('./ssl/default.key'),cert: fs.readFileSync('./ssl/default.cer')
    };

var server = https.createServer(options,function (request,response) {
        // ...
    });
server.addContext('foo.com',{
    key: fs.readFileSync('./ssl/foo.com.key'),cert: fs.readFileSync('./ssl/foo.com.cer')
});

server.addContext('bar.com',{
    key: fs.readFileSync('./ssl/bar.com.key'),cert: fs.readFileSync('./ssl/bar.com.cer')
});
var options = {
        hostname: 'www.example.com',port: 443,path: '/',method: 'GET'
    };

var request = https.request(options,function (response) {});

request.end();

URL

                           href
 -----------------------------------------------------------------
                            host              path
                      --------------- ----------------------------
 http: // user:pass @ host.com : 8080 /p/a/t/h ?query=string #hash
 -----    ---------   --------   ---- -------- ------------- -----
protocol     auth     hostname   port pathname     search     hash
                                                ------------
                                                   query
url.parse('http://user:pass@host.com:8080/p/a/t/h?query=string#hash');
/* =>
{ protocol: 'http:',auth: 'user:pass',host: 'host.com:8080',port: '8080',hostname: 'host.com',hash: '#hash',search: '?query=string',query: 'query=string',pathname: '/p/a/t/h',path: '/p/a/t/h?query=string',href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash' }
*/
http.createServer(function (request,response) {
    var tmp = request.url; // => "/foo/bar?a=b"
    url.parse(tmp);
    /* =>
    { protocol: null,slashes: null,auth: null,host: null,port: null,hostname: null,hash: null,search: '?a=b',query: 'a=b',pathname: '/foo/bar',path: '/foo/bar?a=b',href: '/foo/bar?a=b' }
    */
}).listen(80);
url.format({
    protocol: 'http:',host: 'www.example.com',search: 'query=string'
});
/* =>
'http://www.example.com/p/a/t/h?query=string'
*/
url.resolve('http://www.example.com/foo/bar','../baz');
/* =>
http://www.example.com/baz
*/

Query String

querystring.parse('foo=bar&baz=qux&baz=quux&corge');
/* =>
{ foo: 'bar',baz: ['qux','quux'],corge: '' }
*/

querystring.stringify({ foo: 'bar',corge: '' });
/* =>
'foo=bar&baz=qux&baz=quux&corge='
*/

Zlib

http.createServer(function (request,response) {
    var i = 1024,data = '';

    while (i--) {
        data += '.';
    }

    if ((request.headers['accept-encoding'] || '').indexOf('gzip') !== -1) {
        zlib.gzip(data,data) {
            response.writeHead(200,{
                'Content-Type': 'text/plain','Content-Encoding': 'gzip'
            });
            response.end(data);
        });
    } else {
        response.writeHead(200,{
            'Content-Type': 'text/plain'
        });
        response.end(data);
    }
}).listen(80);
var options = {
        hostname: 'www.example.com',method: 'GET',headers: {
            'Accept-Encoding': 'gzip,deflate'
        }
    };

http.request(options,function (response) {
    var body = [];

    response.on('data',function () {
        body = Buffer.concat(body);

        if (response.headers['content-encoding'] === 'gzip') {
            zlib.gunzip(body,data) {
                console.log(data.toString());
            });
        } else {
            console.log(data.toString());
        }
    });
}).end();

Net

net.createServer(function (conn) {
    conn.on('data',function (data) {
        conn.write([
            'HTTP/1.1 200 OK','Content-Type: text/plain','Content-Length: 11','','Hello World'
        ].join('\n'));
    });
}).listen(80);
var options = {
        port: 80,host: 'www.example.com'
    };

var client = net.connect(options,function () {
        client.write([
            'GET / HTTP/1.1','User-Agent: curl/7.26.0','Host: www.baidu.com','Accept: */*',''
        ].join('\n'));
    });

client.on('data',function (data) {
    console.log(data.toString());
    client.end();
});

灵机一点

小结

开门红

var child_process = require('child_process');
var util = require('util');

function copy(source,target,callback) {
    child_process.exec(
        util.format('cp -r %s/* %s',source,target),callback);
}

copy('a','b',function (err) {
    // ...
});

API走马观花

Process

Child Process

Cluster

应用场景

如何获取命令行参数

function main(argv) {
    // ...
}

main(process.argv.slice(2));

如何退出程序

try {
    // ...
} catch (err) {
    // ...
    process.exit(1);
}

如何控制输入输出

function log() {
    process.stdout.write(
        util.format.apply(util,arguments) + '\n');
}

如何降权

http.createServer(callback).listen(80,function () {
    var env = process.env,uid = parseInt(env['SUDO_UID'] || process.getuid(),10),gid = parseInt(env['SUDO_GID'] || process.getgid(),10);

    process.setgid(gid);
    process.setuid(uid);
});

如何创建子进程

var child = child_process.spawn('node',[ 'xxx.js' ]);

child.stdout.on('data',function (data) {
    console.log('stdout: ' + data);
});

child.stderr.on('data',function (data) {
    console.log('stderr: ' + data);
});

child.on('close',function (code) {
    console.log('child process exited with code ' + code);
});

进程间如何通讯

/* parent.js */
var child = child_process.spawn('node',[ 'child.js' ]);

child.kill('SIGTERM');

/* child.js */
process.on('SIGTERM',function () {
    cleanUp();
    process.exit(0);
});
/* parent.js */
var child = child_process.spawn('node',[ 'child.js' ],{
        stdio: [ 0,1,2,'ipc' ]
    });

child.on('message',function (msg) {
    console.log(msg);
});

child.send({ hello: 'hello' });

/* child.js */
process.on('message',function (msg) {
    msg.hello = msg.hello.toUpperCase();
    process.send(msg);
});

如何守护子进程

/* daemon.js */
function spawn(mainModule) {
    var worker = child_process.spawn('node',[ mainModule ]);

    worker.on('exit',function (code) {
        if (code !== 0) {
            spawn(mainModule);
        }
    });
}

spawn('worker.js');

小结

回调

function heavyCompute(n,callback) {
    var count = 0,i,j;

    for (i = n; i > 0; --i) {
        for (j = n; j > 0; --j) {
            count += 1;
        }
    }

    callback(count);
}

heavyCompute(10000,function (count) {
    console.log(count);
});

console.log('hello');

-- Console ------------------------------
100000000
hello
setTimeout(function () {
    console.log('world');
},1000);

console.log('hello');

-- Console ------------------------------
hello
world
function heavyCompute(n) {
    var count = 0,j;

    for (i = n; i > 0; --i) {
        for (j = n; j > 0; --j) {
            count += 1;
        }
    }
}

var t = new Date();

setTimeout(function () {
    console.log(new Date() - t);
},1000);

heavyCompute(50000);

-- Console ------------------------------
8520

代码设计模式

函数返回值

var output = fn1(fn2('input'));
// Do something.
fn2('input',function (output2) {
    fn1(output2,function (output1) {
        // Do something.
    });
});

遍历数组

var len = arr.length,i = 0;

for (; i < len; ++i) {
    arr[i] = sync(arr[i]);
}

// All array items have processed.
(function next(i,len,callback) {
    if (i < len) {
        async(arr[i],function (value) {
            arr[i] = value;
            next(i + 1,callback);
        });
    } else {
        callback();
    }
}(0,arr.length,function () {
    // All array items have processed.
}));
(function (i,count,callback) {
    for (; i < len; ++i) {
        (function (i) {
            async(arr[i],function (value) {
                arr[i] = value;
                if (++count === len) {
                    callback();
                }
            });
        }(i));
    }
}(0,function () {
    // All array items have processed.
}));

异常处理

function sync(fn) {
    return fn();
}

try {
    sync(null);
    // Do something.
} catch (err) {
    console.log('Error: %s',err.message);
}

-- Console ------------------------------
Error: object is not a function
function async(fn,callback) {
    // Code execution path breaks here.
    setTimeout(function () {
        callback(fn());
    },0);
}

try {
    async(null,function (data) {
        // Do something.
    });
} catch (err) {
    console.log('Error: %s',err.message);
}

-- Console ------------------------------
/home/user/test.js:4
        callback(fn());
                 ^
TypeError: object is not a function
    at null._onTimeout (/home/user/test.js:4:13)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
function async(fn,callback) {
    // Code execution path breaks here.
    setTimeout(function () {
        try {
            callback(null,fn());
        } catch (err) {
            callback(err);
        }
    },0);
}

async(null,data) {
    if (err) {
        console.log('Error: %s',err.message);
    } else {
        // Do something.
    }
});

-- Console ------------------------------
Error: object is not a function
function main() {
    // Do something.
    syncA();
    // Do something.
    syncB();
    // Do something.
    syncC();
}

try {
    main();
} catch (err) {
    // Deal with exception.
}
function main(callback) {
    // Do something.
    asyncA(function (err,data) {
        if (err) {
            callback(err);
        } else {
            // Do something
            asyncB(function (err,data) {
                if (err) {
                    callback(err);
                } else {
                    // Do something
                    asyncC(function (err,data) {
                        if (err) {
                            callback(err);
                        } else {
                            // Do something
                            callback(null);
                        }
                    });
                }
            });
        }
    });
}

main(function (err) {
    if (err) {
        // Deal with exception.
    }
});

域(Domain)

process.on('uncaughtException',function (err) {
    console.log('Error: %s',err.message);
});

setTimeout(function (fn) {
    fn();
});

-- Console ------------------------------
Error: undefined is not a function
function async(request,callback) {
    // Do something.
    asyncA(request,data) {
        if (err) {
            callback(err);
        } else {
            // Do something
            asyncB(request,data) {
                if (err) {
                    callback(err);
                } else {
                    // Do something
                    asyncC(request,data) {
                        if (err) {
                            callback(err);
                        } else {
                            // Do something
                            callback(null,data);
                        }
                    });
                }
            });
        }
    });
}

http.createServer(function (request,response) {
    async(request,data) {
        if (err) {
            response.writeHead(500);
            response.end();
        } else {
            response.writeHead(200);
            response.end(data);
        }
    });
});
function async(request,function (data) {
        // Do something
        asyncB(request,function (data) {
            // Do something
            asyncC(request,function (data) {
                // Do something
                callback(data);
            });
        });
    });
}

http.createServer(function (request,response) {
    var d = domain.create();

    d.on('error',function () {
        response.writeHead(500);
        response.end();
    });

    d.run(function () {
        async(request,function (data) {
            response.writeHead(200);
            response.end(data);
        });
    });
});

陷阱

小结

需求

http://assets.example.com/foo/??bar.js,baz.js
/foo/bar.js
/foo/baz.js
http://assets.example.com/foo/bar.js

第一次迭代

设计

           +---------+   +-----------+   +----------+
request -->|  parse  |-->|  combine  |-->|  output  |--> response
           +---------+   +-----------+   +----------+

实现

var fs = require('fs'),path = require('path'),http = require('http');

var MIME = {
    '.css': 'text/css','.js': 'application/javascript'
};

function combineFiles(pathnames,callback) {
    var output = [];

    (function next(i,len) {
        if (i < len) {
            fs.readFile(pathnames[i],data) {
                if (err) {
                    callback(err);
                } else {
                    output.push(data);
                    next(i + 1,len);
                }
            });
        } else {
            callback(null,Buffer.concat(output));
        }
    }(0,pathnames.length));
}

function main(argv) {
    var config = JSON.parse(fs.readFileSync(argv[0],'utf-8')),root = config.root || '.',port = config.port || 80;

    http.createServer(function (request,response) {
        var urlInfo = parseURL(root,request.url);

        combineFiles(urlInfo.pathnames,data) {
            if (err) {
                response.writeHead(404);
                response.end(err.message);
            } else {
                response.writeHead(200,{
                    'Content-Type': urlInfo.mime
                });
                response.end(data);
            }
        });
    }).listen(port);
}

function parseURL(root,url) {
    var base,pathnames,parts;

    if (url.indexOf('??') === -1) {
        url = url.replace('/','/??');
    }

    parts = url.split('??');
    base = parts[0];
    pathnames = parts[1].split(',').map(function (value) {
        return path.join(root,base,value);
    });

    return {
        mime: MIME[path.extname(pathnames[0])] || 'text/plain',pathnames: pathnames
    };
}

main(process.argv.slice(2));
    http://assets.example.com/foo/bar.js,foo/baz.js

第二次迭代

设计

 发送请求       等待服务端响应         接收响应
---------+----------------------+------------->
         --                                        解析请求
           ------                                  读取a.js
                 ------                            读取b.js
                       ------                      读取c.js
                             --                    合并数据
                               --                  输出响应
发送请求 等待服务端响应 接收响应
---------+----+------------------------------->
         --                                        解析请求
           --                                      检查文件是否存在
             --                                    输出响应头
               ------                              读取和输出a.js
                     ------                        读取和输出b.js
                           ------                  读取和输出c.js

实现

function main(argv) {
    var config = JSON.parse(fs.readFileSync(argv[0],request.url);

        validateFiles(urlInfo.pathnames,pathnames) {
            if (err) {
                response.writeHead(404);
                response.end(err.message);
            } else {
                response.writeHead(200,{
                    'Content-Type': urlInfo.mime
                });
                outputFiles(pathnames,response);
            }
        });
    }).listen(port);
}

function outputFiles(pathnames,writer) {
    (function next(i,len) {
        if (i < len) {
            var reader = fs.createReadStream(pathnames[i]);

            reader.pipe(writer,{ end: false });
            reader.on('end',function() {
                next(i + 1,len);
            });
        } else {
            writer.end();
        }
    }(0,pathnames.length));
}

function validateFiles(pathnames,callback) {
    (function next(i,len) {
        if (i < len) {
            fs.stat(pathnames[i],stats) {
                if (err) {
                    callback(err);
                } else if (!stats.isFile()) {
                    callback(new Error());
                } else {
                    next(i + 1,pathnames);
        }
    }(0,pathnames.length));
}

第三次迭代

设计

实现

var cp = require('child_process');

var worker;

function spawn(server,config) {
    worker = cp.spawn('node',[ server,config ]);
    worker.on('exit',function (code) {
        if (code !== 0) {
            spawn(server,config);
        }
    });
}

function main(argv) {
    spawn('server.js',argv[0]);
    process.on('SIGTERM',function () {
        worker.kill();
        process.exit(0);
    });
}

main(process.argv.slice(2));
function main(argv) {
    var config = JSON.parse(fs.readFileSync(argv[0],port = config.port || 80,server;

    server = http.createServer(function (request,response) {
        ...
    }).listen(port);

    process.on('SIGTERM',function () {
        server.close(function () {
            process.exit(0);
        });
    });
}

第四次迭代

设计

- deploy/
    - bin/
        startws.sh
        killws.sh
    + conf/
        config.json
    + lib/
        daemon.js
        server.js

实现

#!/bin/sh
if [ ! -f "pid" ]
then
    node ../lib/daemon.js ../conf/config.json &
    echo $! > pid
fi
#!/bin/sh
if [ -f "pid" ]
then
    kill $(tr -d '\r\n' < pid)
    rm pid
fi

后续迭代

小结

原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_66422.html