背景随着node的出现和发展,前端承担了越来越多的职责。前端也有越来越多的场景需要使用批量运行脚本使用爬虫或者接口定时同步数据到DB线上配置文件,批量运行数据文件生成并发布到线上实际上会影响业务,所以需要一套高可靠性的批处理管理系统,及时报警。本文将批处理系统封装为一个npm模块,使用方便,提供了一个简单的web管理系统进行管理。如何使用1.安装npminstallschedule_task_monitor--savegihublinkhttps://github.com/WilsonLiu9...2.导入模块和输入参数//init.jsvar{run,eventEmitter,app}=require('sche_task_monitor');run({mysql_config:{//mysql连接配置host:'localhost',port:'3306',user:'root',password:'1234',database:'db_lct_schedule',//请先创建数据库},task_root_path:'/data/web/schedule/task/',//任务脚本根路径defaultRtx:'wilsonsliu'//默认报警传输对象});//启动节点init.js3。启动web管理系统web系统在8017端口打开:http://127.0.0.1:8017node./node_mudule/sche_task_monitor/webSystem/app.js4。新任务文件的任务名称为test。这时候需要在/data/web/schedule/task/test目录下新建一个,新建/data/web/schedule/task/test/index.js;varfs=require('fs-extra');varpath=require('path');console.log('testing')fs.writeFileSync(path.join(__dirname,'publish','a.txt'),'sdsds')5.web系统新增任务在web系统新增任务,例如配置如下{task_name:'test',rule:'*/30*****',rtx_list:'wilsonliuxyz@gmail.com;test@qq.com',//报警相关负责人title:'Test',description:'测试说明'}此时,批运行管理系统会按照设定的任务规则运行,每15S执行一次事件。system模块暴露了eventEmitter,可以通过监听task_start,task_end,通知事件来执行用户相应的代码。eventEmitter.on('task_start',function({task_name,task_version}){});//任务结束eventEmitter.on('task_end',function({task_name,exit_code,task_version,error_log_list}){});//监听闹钟eventEmitter.on('notify',function({title,content,task_name,notify_list}){});注意告警模块需要通过监听notify事件来设计自己的告警。用户可以选择自己方便的方式,最好能够通过多种方式发送告警。比如微信、邮箱、邮箱等等。实现高时效notify如果没有task_name,则notify_list,使用传入的defaultRtx进行初始化。发布批量运行生成的文件对于任务中生成的文件,规范放在对应的任务发布目录下。实现自己的发布功能,在任务中调用。任务退出代码exit_code任务正常退出。批量运行模块会收到exit_code为0,因为异常退出会收到1。当退出代码不为零时,将触发警报。用户可以使用process.exit(101)触发报警task_name设计task_name唯一,可以写成monitor/logline,那么执行路径就变成monitor/logline/index.js。即入口文件的拼接方式可以是多级的。不建议太深,不方便管理和查看。非节点批处理脚本?对于非节点批量运行的脚本,我们可以在入口文件中再次调用其他脚本。系统设计概述使用crontab确定方向说到批量运行,首先想到的肯定是使用linux自带的crontab来完成定时批量运行的目的。然而,依靠这种方法存在以下问题。每个任务都需要去crontab添加一条新的规则。随着时间的推移,很难维护任务执行的各种告警。实现难度大,漏执行,超时,异常退出等。使用node开源模块node-schedule作为前端,当然能用JS实现的都用JS(node)实现。Node本身有丰富的npm模块,node-schedule是一个拥有4300+star的定时任务模块。可以像crontab一样编辑定时规则,在规则指定的时间执行相应的回调函数。通过表t_task_list管理任务,主要录入每个任务的rule、timeout、last_start_time、last_end_time、last_warning_time,实现任务管理通过批运行系统,统一任务管理和监控,实现对各种任务的报警和针对性.批量运行系统的目的是管理所有批量运行的任务,并对批量运行的任务进行监控。同时,因为会直接影响线上系统,所以在稳定性方面有很高的要求。高稳定性、弱入侵(尽量减少批量运行系统对相应任务的干扰)方便的基础服务定时运行各种异常情况下告警日志输出版本备份任务执行放在任务目录下进行管理,每个任务的关键信息,如task_name和rule,都被录入到表t_task_list中进行管理。下面的例子中,task_name为gold,那么批运行系统在数据库中找到task_name=gold这条记录,根据对应的规则挂到定时器上,gold/index.js就是对应的任务入口文件,系统通过节点任务/gold/index.js执行特定任务。src├──index.js//入口文件├──lib│├──execTask.js//执行特定任务的代码│├──hook.js//开始和结束任务的钩子函数│├──initDB.js//初始化DB│└──monitorHelper.js//5个监控助手├──webSystem//GUI批处理系统├──task//指定此目录为任务根目录|└──gold//一个特定的定时任务|└──index.js//某个任务的入口文件|└——logs//本次任务留下的日志文件│└──201711//某月│├──23_213854.log//根据day_HHmmss建立目录存放历史版本|└——publish//每个task发布的文件夹在线发布时需要手动调用publish函数。任务退出后,批量运行系统会自动发布该目录下的文件备份到history目录|└——history//目录下的文件以gold_profit.201711231015.json的形式保存,即名称+时间(精确到分钟)+后缀│└──201711//某月历史发布文档│├──23_213854//根据day_HHmmss建立目录存放历史版本││├──currentYearPrice.jsonweb管理终端展示系统特性介绍和原理介绍高稳定性模块化和简洁性。通过对批量运行系统进行模块化,保证各模块代码的精简和健壮性,提高了批量运行系统的稳定性。批量运行系统与任务隔离。批运行系统通过child_process.spawn子进程运行任务,保证批运行系统与具体任务的隔离,任务异常不会导致批运行系统崩溃。最后,pm2管理。任何系统都很难避免挂掉。如果系统挂了,任务会通过pm2自动重启。弱侵入任务使用child_process.spawn执行,任务入口文件指定task/task_name/index.js,相当于nodetask/task_name/index.js。优点:批处理脚本任务代码不需要修改,可以选择自己喜欢的方式编写代码。如果不通过require引入,批量运行系统不需要每次更新任务都重启,只需要部署任务的代码文件即可。有两点需要注意,任务不能一直挂起,任务完成后需要退出。主要需要注意的检查项是mysql连接没有关闭等。批处理系统通过监听任务子进程的关闭事件,知道任务执行是否完成。当exit_code不为0时,批处理系统会报警(关于process.exit,可以阅读文末参考链接3)。未捕获的异常导致的退出会吐出exit_code=1。捕获到异常后,可以通过进程.exit(exit_code)指定自己定义的exit_code(100以内,状态码为批运行系统预留)子任务正常执行时会吐出0。函数,并支持crontab语法规则。主要使用scheduleJob接口挂载定时任务。系统启动时,到数据库的t_task_list中取出所有任务的task_name和rule数据,遍历挂载。同时,挂载的句柄存储在全局对象G_task_schedule_list中。constschedule=require('node-schedule');//全局存储任务定时器句柄G_task_schedule_listG_task_schedule_list[task_name]=schedule.scheduleJob(rule,function(){//在回调函数中执行具体任务execTask(task_name,app);});各种异常情况报警在以下5种情况下,任务相关责任人将被报警。任务执行超时报警(t_task_list表中的每个任务都可以指定超时时间,以秒timeout为单位)exit_code不为0,即异常退出,漏执行报警(cron-parser解析规则得到最后一次运行的时间,即通过与任务的last_start_time比较判断是否错过执行)数据库中的任务被删除(通过当前挂载的任务G_task_schedule_list与数据库中的任务进行比较,发现是否有任务被删除)内部异常批量运行模块的日志输出。父进程监听子进程的stdout、stderr两个输出流,得到子进程的日志输出。日志会存放在task/logs/YYYYMM/DD/HHmmss.log目录下,按照任务的执行时间存放,stderr信息会存放在数据库中(为了保护批处理系统,只有将输入前100个条目)。用于UI界面显示和报警时的输出。如果用户需要详细的日志,还是需要查阅日志文件。stderr可以通过console.error输出。另外,如果进程异常退出,也会输出到stderr。建议捕获异常后通过console.error输出。版本备份每个任务执行时,可以将文件写入对应任务的发布目录。如果需要发布上线,可以手动调用Helper中的发布功能。对于目前与其他系统强耦合的发布功能,可以使用接口的形式批量调用,在接口返回发布的内容,任务写入发布目录进行版本备份。每个task退出后,批处理系统会检查它的publish是否为空。如果不为空,则移动到history目录,存放在有版本号的文件夹中,方便备份查看。监控助手批量运行系统挂载监控助手,每3秒执行一次,实现准实时监控。帮手1:已有任务:数据库更新规则,取消定时任务,设置定时任务挂载新规则;新任务:按规则挂载助手2:用户设置task_status为2,然后杀掉当前进程助手3:根据数据库中的超时字段,进行超时提醒助手4:任务未执行,报警通知助手5:任务在数据库中删除,提醒用户任务初始化和结束。hook.js包含startExecTask和endExecTask函数在任务开始和结束时运行。startExecTask执行以下操作清除任务的发布文件夹task/task_name/publish更新任务表中的last_start_time和task_version(任务的版本号是根据运行时间生成的consttask_version=moment().format('YYYYMM/DD/HHmmss');)在t_task_exec_list中插入一条任务执行记录endExecTask执行下面的动作,设置退出事件和退出码输入日志,发布的文件路径数组)总结作为基础设施的批运行系统与其他系统最大的区别就是对稳定性要求高,监控告警精准,避免任务中出现各种情况导致的线上问题,不过后面Sleep就知道了。本系统的设计基本达到了设计目标,同时提供了一个易于管理的web系统,可以方便的管理任务和查看历史执行情况。参考资料node-schedule解析crontab规则cron-parserprocess对象和exit_code
