设为首页 收藏本站
云服务器等爆品抢先购,低至4.2元/月
查看: 757|回复: 0

[经验分享] 当Shell遇上了NodeJS

[复制链接]

尚未签到

发表于 2017-2-21 10:28:35 | 显示全部楼层 |阅读模式
     (转载): http://www.infoq.com/cn/articles/yph-shell-meet-nodejs
       无论在传统的企业级系统维护还是在互联网运维中,Shell脚本的编写与维护常常必不可少,在系统管理员或开发人员工作中占比重比较大的一部分。Shell脚本的严格语法格式对于一般的运维人员来说,常常会在一不留神下而抓狂或查找半天才发现是因为多了或少了一个空格或某语包括号不匹配而导致的错误,不但大大的浪费了脚本维护人员的工作时间,还可能影响到工程进度甚至项目的发布里程碑等。当然,对于非纯Geek来说,最重要的还是影响心情,特别是对于一些较复杂的脚本需求,更是必须小心谨慎,因此越来越多的开发人员必须借助于Python、Perl、Ruby等相关的脚本语言来实现,但是常由于平台的特性或者语言的限制,对系统级的命令调用或者异常处理有限制,最终解决起来并不是十分优雅。 NodeJS的出现或许会给这些开发人员带来一些新的选择。
NodeJS从诞生起发展非常迅速,社区活动非常活跃,目前扩展模块达到1500多个,并且每天都有不同的模块提交。它是构建在JavaScript引擎V8之上的JavaScript环境,它采用基于单线程的异步事件驱动I/O模型,具有非常高的性能,同时能够支持多种平台。日前国外的很多大的软件或互联网公司如Microsoft,ebay,yahoo等都在使用NodeJS,国内的网易,淘宝,新浪等互联网企业也有很多分享和成功的线上案例应用。言归正传,希望下文的内容能给不熟悉或不喜欢nix平台Shell脚本开发或WIN平台下的批处理编写的工程师带来一些帮助,为简单起见,本文采用Nix平台为示例,WIN平台的用户请参考自行修改或与作者联系。

 

首先,我承认Shell脚本中系统命令再加上sed,awk等瑞士军刀在一起工作已经相当强大,如果你想了解NodeJS的强大之处和如何结合Shell产生强大的工作效率,并且还能具有很好的灵活性,那就让我们继续旅程吧:

示例

先看一段简单的采用Shell 脚本执行一段命令得到其执行时间的脚本diffa.sh:

#!/bin/bash
START=$(date +%s)
# prepare things
du -m /home > /tmp/output
# done  END=$(date +%s)
DIFF=$(( $END - $START ))
echo "化了$DIFF 秒搞定"
chmod +x diff.sh
sh diff.sh
执行上面的脚本后,结果如下:

相关厂商内容

百度技术沙龙第四十四期: 大数据面面观(2013年11月23日周六)

深度案例解读,了解100件案例背后的思考

QClub大连站:软件开发过程中的平台、技术选择(2013年11月23日 周六)

Hadoop年度最大盛会,Moses创始人首次来华,中国Hadoop技术峰会

Top100summit案例分享:且看微信之海外精益创业

 







 



化了0 秒搞定
用户首次执行一般会耗时几秒,多次执行的结果可能会在0-1秒之间随机显示。因为du的输出重定向,整个脚本的执行时间非常短,并且脚本中采用的是秒数级别的范围,如果需要得到这个脚本的准确执行时间只能用纳秒来进行,并在Shell做除法运算,把脚本修改一下diffb.sh。

START=$(date +%s%N)
du -m /home/ >/tmp/output
END=$(date +%s%N)
DIFF=$(($END - $START))
SUM=`expr $DIFF / 1000000`
echo "化了$SUM MS搞定"
执行一下上面的diffb 脚本就可以得到运行的结果了,需要提醒的是上面的脚本中各表达式的格式都是即定的,如果开发人员不小心多了一个空格或少了一空格,都将导致脚本错误。下面采用NodeJS来试试看,首上下载与安装NodeJS环境,过程非常简单,具体请参考官方网站或直接apt-get 之类的操作。编写如下diffc.js脚本:

#!/usr/bin/env node
var util = require('util'),
spawn = require('child_process').spawn,
ls = spawn('du', ['-m', '/home/']);
var start = +new Date();
ls.stdout.on('data', function (data) {
//console.log('stdout: ' + data);
});
ls.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
ls.on('exit', function (code) {
var end = +new Date();
console.log(end - start);
});

注:上面require中引用的都是系统内置模块,spawn的格式为spawn(command, [args], [options]),其他请参阅官方文档。
同样,chmod +x 对脚本赋予执行权限,执行脚本./diff.js,结果如下:

1113
上面示例中显示的时间直接是毫秒级别,代码格式没有严格的限制,流程的控制也会更加灵活,特别是在异常情况下,可以根据用户的需求处理更小的细节。当然,我承认这个示例需求有些诡异,但是做这样的比较,并不是说要二者一决高下,只是换一种前端攻城师喜欢的方法去实现一些系统运维需求。在这里NodeJS脚本本身也是依赖于系统Shell的强大基础之上。

深入一点

以上示例可以看到,在Shell环境中,NodeJS内置模块实现常用的功能是即方便又灵活,Linux Shell环境中比较强大的功能之一就是支持输入输出重定向功能,用符号<和>来表示。0、1和2分别表示标准输入、标准输出和标准错误信息输出,用来指定需要重定向的标准输入或输出,比如 2>error.log表示将错误信息输出到文件err.log中。类似的,NodeJS中可以直接采用超复杂的命令来搞定,一般对于我们这些非系统管理员有一定的难度,下面引入强大点的模块procstreams,它可以实现输出流重定向等功能,首先用户需要执行npm install procstreams安装模块,编写示例如下wc.js:

#!/usr/bin/env node
var p = require('procstreams');
p('cat app.log').pipe('wc -l').data(function (stdout, stderr) {
console.log(stdout);
});
wc.js脚本代码是借助于Shell命令实现统计app.log的行数,相当于Shell环境中的cat app.log | wc -l功能,输出的结果可以再根据需要再进行灵活处理,另外它还支持then、and和or等操作,类似Shell脚本中的;、&& 和||操作。在实现复杂或交互的功能时,甚至可以完全采用交互的方式进行操作输入。
另外,用户执行脚本的时候还需要处理复杂一些的参数对应,node-optimist 及 isaacs's nopt 之类的模块可以非常简单的帮助攻城师实现这样的功能,如实现根据用户的输入的参数执行需要的系统命令,并可以做相关的逻辑处理的opt.js:

#!/usr/bin/env node
var util = require('util'),
spawn = require('child_process').spawn;
var argv = require('optimist').argv;
var cmd = argv.cmd;
var args = argv.args
var option = argv.opt
var ls = spawn(cmd, [args, option]);
ls.stdout.on('data', function (data) {
if (!data || !! data) console.log(' i believe it');
});
ls.stderr.on('data', function (data) {
console.log('It\'s a miracle!');
});
ls.on('exit', function (code) {
console.log('it.justHappened();');
});
用户使用如下对应格式执行代码:./opt.js --cmd=ls --args=/m --opt=/home,然后只需要在代码相关处添加对应的逻辑代码,把注意力放在业务层,采用js的流程控制实现业务逻辑的分离。

实际应用

在企业线上或系统运维中,常需要对一些进程进行监控和报警,以便通知相关系统管理人员,如下Shell脚本 agenta.sh实现了对tomcat6进程监控,如果不存在自动重启。

#!/bin/sh
pid=`ps aux| grep "tomcat6" | grep -v grep | sed -n  '1P' | awk '{print $2}'`
if [ -z $pid ]; then
echo "begin restart,please waiting..."
sudo /etc/init.d/tomcat6 restart
exit 1
else
echo -e "exist ,don't need restart"
fi
脚本编写人员在经过一番努力与折腾后,完成了代码编写与调试工作,然后需要通过系统的crontab功能添加如0-59/2 * * * * sh agent.sh的定时任务,如果系统管理员把crontab的权限给禁用了,那就需要得到系统管理员的帮助了。下面使用Nodejs来实现同样的功能,先假设读者对grep、sed和awk等常用命令的使用有大概了解,代码如下agentb.js:

var p = require('procstreams');
var exec = require('child_process').exec;
setInterval(function () {
exec("ps aux| grep 'tomcat6' | grep -v grep | sed -n  '1P' | awk '{print $2}'", function (err, output) {
if (err) throw err;
if (output.length > 0) console.log('exist,don\'t need restart');
else exec('sudo /etc/init.d/tomcat6 restart', function (err2, out2) {});
})
}, 1000 * 60 * 2);
示例代码中setInterval的函数的作用通过设置一个回调函数和间隔执行时间来实现定时监控。运行代码后,同样可以实现进程监控的功能,也许你会说上面的Shell命令还是很多的。因为你觉得直接使用Shell脚本会更简单,可是如果你经历过为空格或配置之类的调试过程,或者需求更加复杂时,采用NodeJS会让你觉得非常轻松。更重要的是,编写脚本后,在执行脚本时你可以直接通过chrome debug 工具设置断点与单步调试,或者在chrome 浏览器上进行图形化调试等操作,具体请参考node-inspector。现在,agentb.js代码中的Shell命令还是太长了太复杂,调试起来也不太方便,使用procstreams做一下简化,实现代码agentc.js如下:

var p = require('procstreams');
setInterval(function () {
p("ps aux").pipe('grep tomcat6').pipe('grep -v grep').pipe('sed -n 1P').pipe("awk $2")
.data(function (stdout, stderr) {
if (stderr) throw stderr;
if (stdout.length > 0) {
console.log('exist,don\'t need restart');
} else {
console.log('restart,waiting...');
p('sudo /etc/init.d/tomcat6 restart', function (stdout, stderror) {
console.log(stdout);
});
}
});
}, 1000 * 60 * 2);
agentc代码中通过pipe操作可以实现对每个步骤的输入进行详细的跟踪与调试,但是脚本中还是需要对系统的很多内置命令有大概的了解,需要对操作系统的相关功能或语法格式比较熟悉,使用起来还是有点不习惯。攻城师都喜欢编程时能控制住自己把握的,或者在使用简单的命令的情况下,就能实现需要的功能,再次简化代码后得到agentd.js

var p = require('procstreams');
var serviceName = 'tomcat6';
var interval = 1000 * 60 * 2;
setInterval(function () {
p("ps aux").pipe('grep ' + serviceName).data(function (stdout, stderr) {
if ( !! stdout && stdout.indexOf(serviceName) == 0) {
console.log('exist,don\'t need restart');
} else {
console.log('restart,waiting...');
p('sudo /etc/init.d/tomcat6 restart', function (stdout, stderror) {});
}
});
}, interval);
在经过这次修改之后,对系统命令的掌握程度要求明显更低了,题外话,用户对系统命令了解的越详细越好,但如果使用简单即美的指导去实现同样的需求,何乐而不为。代码中serviceName 和interval 参数可以通过node-optimist模块动态给定,这样就可以实现一份代码监控多个进程,并且不需要系统管理员的帮助去添加定时任务的操作。当然,希望这样操作不会影响系统功能或在权限范围内。

总结

尽管Linux的Shell环境编程非常的强大,但是编写或调试Shell脚本时常令人抓狂不已,也没有很好的图形化调试工具。当然脚本较复杂时,尤其在需求跨平台时,脚本改动比常较大,日前,开发人员需要根据平台的不同,准备多套脚本代码,如tomcat,apache等,如果采用简单Shell和NodeJS结合编程,或许只需要把平台相关的命令提取出来,只需较少改动就能实现跨平台,可以大大提高工作效率与减少浪费攻城师的时间。个人认为,采用二者结合的方式具有以下优点:


  • 采用v8引擎,轻量级模块,较好跨平台性,较底层的系统操作,在系统监控运维等方面具有明显优势,
  • 采用事件驱动非阻塞IO模型,无线程上下文切换和锁操作,, 可利用多核CPU计算,性能较高,
  • 开放源代码,社区活跃,模块丰富,底层的扩展实现也较方便。

随着NodeJS不断发展和成熟,国内外厂商越来越多的成功案例与分享,在企业级和互联网系统应用开发和维护中具有更广阔的前景。

参考资料


  • http://NodeJS.org
  • http://www.infoq.com/cn/articles/tyq-NodeJS-event
  • http://www.catonmat.net/blog/
  • http://www.cNodeJS.org

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-345142-1-1.html 上篇帖子: nodejs 之模拟登录 下篇帖子: NODEJS(15)Platform
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表