|
<
七天教会NodeJS(四)一边读与一边输出呼应、保护历程(child process、spawn)、功用-机能-不变性-代码布置
文章目次
总结:
- 合并静态文件示例
- 本章小结
- process补充
- SIGKILL 是报告历程要立刻停止的旌旗灯号,幻想状况下,其举动相似于 process.exit()。
- SIGTERM 是报告历程要一般停止的旌旗灯号。它是从历程办理者(如 upstart 或 supervisord)等收回的旌旗灯号。
- 能够从法式内乱部另外一个函数中收收此旌旗灯号:
- process.kill(process.pid, 'SIGTERM')
复造代码 或从另外一个正正在运转的 Node.js 法式、或从体系中运转的其他任何的使用法式(能明白要停止的历程的 PID)。
- Child Process
- 民圆文档: http://nodejs.org/api/child_process.html
- 利用child_process模块能够创立战掌握子历程。
- 该模块供给的API中最核心的是.spawn,其他API皆是针对特定利用场景对它的进一步启拆,算是一种语法糖。
- 利用.spawn(exec, args, options)办法,创立子历程,该办法撑持三个参数。
- 第一个参数是施行文件途径,能够是施行文件的相对或尽对途径,也能够是按照PATH情况变量能找到的施行文件名。
- 第两个参数中,数组中的每一个成员皆按挨次对应一个号令止参数。
- 第三个参数可选,用于设置子历程的施行情况取举动。
- 第一次迭代(功用)
- 效劳器会起首阐发URL,获得恳求的文件的途径战范例(MIME)。
- 利用号令止参数通报JSON设置文件途径,进口函数卖力读与设置并创立效劳器。
- 进口函数完好形貌了法式的运转逻辑,此中剖析URL战合并文件的具体完成启拆正在别的两个函数里。
- 剖析URL时先将一般URL转换为了文件合并URL,使得两种URL的处置处罚方法能够分歧。
- 然后,效劳器会读与恳求的文件,并按挨次合并文件内乱容。
- 合并文件时利用同步API读与文件,避免效劳器果等待磁盘IO而发作壅闭。
- 最初,效劳器返反应应,完成对一次恳求的处置处罚。
- 逻辑缺点:
- http://assets.example.com/foo/bar.js,foo/baz.js
复造代码 经过阐发以后我们会发明成绩出正在/被主动交换/??那个举动上,而那个成绩我们能够到第两次迭代时再处理。
- 第两次迭代(机能)
- 第两版代码正在查抄了恳求的一切文件能否有用以后,立刻便输出了呼应头,并接着一边按挨次读与文件一边输出呼应内乱容。
- 而且,正在读与文件时,第两版代码间接利用了只读数据流去简化代码。
- 效劳器自己的功用战机能曾经获得了开端满意
- 第三次迭代(不变性)
- 从工程角度上讲,出有尽对牢靠的体系。即便第两次迭代的代码经过重复查抄后能确保出有bug,也很易道能否会由于NodeJS自己,大概是操纵体系自己,以至是硬件自己招致我们的效劳器法式正在某一天挂失落。因而普通消费情况下的效劳器法式皆配有一个保护历程,正在效劳挂失落的时分立刻重启效劳。普通保护历程的代码会近比效劳历程的代码简朴,从几率上能够包管保护历程更易挂失落。假如再做得松散一些,以至保护历程本身能够正在本人挂失落时重启本人,从而完成单保险。
- 因而正在本次迭代时,我们先操纵NodeJS的历程办理机造,将保护历程做为女历程,将效劳器法式做为子历程,并让女历程监控子历程的运转形态,正在其非常退出时重启子历程。
- 步调
- 扼守护历程的代码保留为daemon.js,以后我们能够经由过程node daemon.js config.json启动效劳,而保护历程会进一步启动战监控效劳器历程
- 让保护历程正在领受到SIGTERM旌旗灯号时停止效劳器历程
- 而正在效劳器历程那一端,一样正在支到SIGTERM旌旗灯号时先停失落HTTP效劳再一般退出。
- 第四次迭代(代码布置及效劳器掌握)
1. 年夜示例
进修讲求的是教致使用战举一反三。至此我们曾经别离引见了NodeJS的许多常识面,本章做为最初一章,将完好天引见一个利用NodeJS开拓Web效劳器的示例。
需供
我们要开拓的是一个简朴的静态文件合并效劳器,该效劳器需求撑持相似以下格局的JS或CSS文件合并恳求。
- http://assets.example.com/foo/??bar.js,baz.js
复造代码 正在以上URL中,??是一个分开符,之前是需求合并的多个文件的URL的大众部分,以后是利用,分开的不同部分。因而效劳器处置处罚那个URL时,返回的是以下两个文件按挨次合并后的内乱容。
别的,效劳器也需求能撑持相似以下格局的一般的JS或CSS文件恳求。
- http://assets.example.com/foo/bar.js
复造代码 以上便是全部需供。
第一次迭代
快速迭代是一种没有错的开拓方法,因而我们正在第一次迭代时先完成效劳器的根本功用。
设想
简朴阐发了需供以后,我们大致会获得以下的设想计划。
- +---------+ +-----------+ +----------+
- request -->| parse |-->| combine |-->| output |--> response
- +---------+ +-----------+ +----------+
复造代码 也便是道,效劳器会起首阐发URL,获得恳求的文件的途径战范例(MIME)。然后,效劳器会读与恳求的文件,并按挨次合并文件内乱容。最初,效劳器返反应应,完成对一次恳求的处置处罚。
别的,效劳器正在读与文件时需求有个根目次,而且效劳器监听的HTTP端心最好也没有要写逝世正在代码里,因而效劳器需求是可设置的。
完成
按照以上设想,我们写出了初版代码以下。
- 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], function (err, data) {
- if (err) {
- callback(err);
- } else {
- output.push(data);
- next(i + 1, len);
- }
- });
- } else {
- callback(null, Buffer.concat(output));
- }
- }(0, pathnames.length));
- }
- //1.进口函数main
- //2.号令止中输进的是`node server.js config.json`第一个参数是 `node` 号令的完好途径。第两个参数是正被施行的文件的完好途径。一切其他的参数从第三个地位开端
- function main(argv) {
- //argv[0]是`node` 号令的完好途径
- var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')),
- //效劳器正在读与文件时需求有个根目次,而且效劳器监听的HTTP端心最好也没有要写逝世正在代码里,因而效劳器需求是可设置的。
- root = config.root || '.',
- port = config.port || 80;
-
- //创立了一个HTTP效劳器并监听port端心
- http.createServer(function (request, response) {
- //挪用URL剖析函数,获得恳求的文件的途径战范例(MIME)
- var urlInfo = parseURL(root, request.url);
-
- //挪用函数,效劳器会读与恳求的文件,并按挨次兼并文件内乱容
- combineFiles(urlInfo.pathnames, function (err, data) {
- if (err) {
- response.writeHead(404);
- response.end(err.message);
- } else {
- //最初,效劳器返反响应,完成对一次恳求的处置。
- response.writeHead(200, {
- 'Content-Type': urlInfo.mime
- });
- response.end(data);
- }
- });
- }).listen(port);
- }
- //URL剖析函数,获得恳求的文件的途径战范例(MIME)
- 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));
复造代码 p以上代码完好完成了效劳器所需的功用,而且有以下几面值得留意:/p olli利用号令止参数通报JSON设置文件途径,进口函数卖力读与设置并创立效劳器。/lilimark正在号令止中输进的是codenode server.js config.json/code第一个参数是 codenode/code 号令的完好途径。第两个参数是正被施行的文件的完好途径。一切其他的参数从第三个地位开端/mark。/lili进口函数完好形貌了法式的运转逻辑,此中剖析URL战合并文件的具体完成启拆正在别的两个函数里。/lili剖析URL时先将一般URL转换为了文件合并URL,使得两种URL的处置处罚方法能够分歧。/lili合并文件时利用同步API读与文件,避免效劳器果等待磁盘IO而发作壅闭。/li/ol p我们能够把以上代码保留为codeserver.js/code,以后就能够经由过程codenode server.js config.json/code号令启动法式,因而我们的初版静态文件合并效劳器便顺遂竣工了。/p p别的,以上代码存正在一个没有那末较着的逻辑缺点。例如,利用以下URL恳求效劳器时会有欣喜。/p- http://assets.example.com/foo/bar.js,foo/baz.js
复造代码 p经过阐发以后我们会发明成绩出正在code//code被主动交换code/??/code那个举动上,而那个成绩我们能够到第两次迭代时再处理。/p h3a id="_222"/a第两次迭代/h3 p正在第一次迭代以后,我们曾经有了一个可事情的版本,满意了功用需供。接下去我们需求从机能的角度动身,看看代码另有哪些改良余天。/p h4a id="_226"/a设想/h4 p把codemap/code办法换成codefor/code轮回大要会更快一些,但初版代码最年夜的机能成绩存正在于从读与文件到输出呼应的历程傍边。我们以处置处罚code/??a.js,b.js,c.js/code那个恳求为例,看看全部处置处罚过程当中耗时正在哪女。/p- 收收恳求 等候效劳端呼应 领受呼应
- ---------+----------------------+------------->
- -- 剖析恳求
- ------ 读与a.js
- ------ 读与b.js
- ------ 读与c.js
- -- 兼并数据
- -- 输出呼应
复造代码 能够看到,初版代码顺次把恳求的文件读与到内乱存中以后,再合并数据战输出呼应。那会招致以下两个成绩:
- 当恳求的文件比力多比力年夜时,串止读与文件会比力耗时,从而推少了效劳端呼应等待工夫。
- 因为每次呼应输出的数据皆需求先完好天缓存正在内乱存里,当效劳器恳求并收数较年夜时,会有较年夜的内乱存开消。
关于第一个成绩,很简单念到把读与文件的方法从串止改成并止。可是别如许做,由于关于机械磁盘而行,由于只要一个磁头,测验考试并止读与文件只会形成磁头频仍颤动,反而降低IO服从。而关于固态硬盘,固然确实存正在多个并止IO通讲,可是关于效劳器并止处置处罚的多个恳求而行,硬盘曾经正在做并止IO了,对单个恳求采取并止IO无同于拆东墙补西墙。因而,准确的做法没有是改用并止IO,而是一边读与文件一边输出呼应,把呼应输出机会提早至读与第一个文件的时辰。如许调整后,全部恳求处置处罚历程变成下边如许。
- 收收恳求 等候效劳端呼应 领受呼应
- ---------+----+------------------------------->
- -- 剖析恳求
- -- 查抄文件能否存正在
- -- 输出呼应头
- ------ 读与战输出a.js
- ------ 读与战输出b.js
- ------ 读与战输出c.js
复造代码 按上述方法处理第一个成绩后,由于效劳器没有需求完好天缓存每一个恳求的输出数据了,第两个成绩也水到渠成。
完成
按照以上设想,第两版代码按以下方法调整了部分函数。
[code]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); validateFiles(urlInfo.pathnames, function (err, 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 <span class="token operator"> |
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请您发送邮箱:Cdnjson@163.com提供相关证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
|