后台服务的业务逻辑中,或多或少需要一些异步去处理的脚本逻辑,例如业务的统计、上报、数据运算,定时监控等等。实现的方法也有很多,用linux自带的crontab,定时清理服务器上的日志就很常用,由于Python处理数据的优势,很多开发童鞋也会用python来跑一些需要运算的脚本,另外还有网上流行的一些脚本框架,一些大公司也有自己研发的框架服务。在最近的项目中,接触到swoole2 与php7 搭配的性能优越,普通4核心CPU,单机压测qps 10000 /s,突生灵感,除了用来搭后端服务,能不能用来搭一个脚本服务呢?
于是归纳了一下以前跑脚本的问题:
代码语言:javascript复制1、crontab:不能实现到秒级,最多到分钟级,这是很多开发的小痛点,当然想实现秒级也是可以滴,这么写:
***** sleep 10;/usr/bin/myshell.sh
***** sleep 20;/usr/bin/myshell.sh
***** sleep 30;/usr/bin/myshell.sh
***** sleep 40;/usr/bin/myshell.sh
***** sleep 50;/usr/bin/myshell.sh
变相实现10秒执行一次,但这做法有点low有木有,而且crontab多了比较离散,缺乏集中管理。当然这里可以用一些
crontab的管理工具来维护。
2、搭一套同步服务框架,走服务请求来跑脚本,这里有个好处,本身的服务的很多方法函数,走脚本服务框架时可以复用。
但传统服务框架thinkphp、CI等性能较低,脚本内的逻辑存在调用下游时,由于下游服务的不可靠性。导致超时阻塞,
服务雪崩,影响其它脚本。
3、其它第三方架构,包括之前用公司内部的一些脚本大师,都不甚好用。要么存在同步阻塞,要么时间粒度太粗,要么缺
少好的监控。
问题理清楚后,需求也就很清晰了:
代码语言:javascript复制1、时间粒度精确到秒。
2、有监控脚本,各脚本运行情况。
3、高性能,多任务同时在跑,不会相互影响。
表简单设计如下:
代码语言:sql复制CREATE TABLE `task` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`task_desc` varchar(256) NOT NULL COMMENT '任务描述',
`task_name` varchar(128) NOT NULL COMMENT '任务名称,对应控制器',
`task_crontab` varchar(128) NOT NULL COMMENT '每隔多久执行一次',
`task_status` tinyint(4) NOT NULL DEFAULT '-1' COMMENT '-1未启用,0等待下次执行,1执行中',
`runnings` smallint(6) NOT NULL DEFAULT '0' COMMENT '正在执行的进程数',
`is_multy` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1允许同时执行多个;0须等待上一次执行完才执行下一次,即使下次执行时间到。',
`exec_times` int(11) NOT NULL DEFAULT '0' COMMENT '已累计执行次数',
`err_times` int(11) NOT NULL DEFAULT '0' COMMENT '累计失败次数',
`last_exec_time` datetime NOT NULL COMMENT '最后执行时间',
`next_exec_time` datetime NOT NULL COMMENT '下一次准备执行时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='任务管理表'
例如下面添加了一条测试任务:
任务的名称对应脚本框架服务中有一个控制器,来处理脚本逻辑。
服务相关事项:
代码语言:javascript复制1、shell起多个task.sh进程去调脚本框架服务的main入口,脚本服务支持1w qps/s,这里起多少个进程视情况定。
eg:#!/bin/bash
while ((1));do
exec main
sleep 1
done
2、用check_task.sh监控脚本task.sh是否运行。
#!/bin/bash
count=`ps aux|grep "task_idc"|grep -v 'grep'|wc -l`
if [ $count -lt 1 ]; then
echo `bash task_idc.sh 2>&1 &`
fi
3、next_exec_time每个任务下一次执行时间,如果有任务这个时间小过了当前时间,所以有任务不健康,这里简单监控下。
conn="mysql -h${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME}"
select_sql="select id,task_crontab,unix_timestamp(next_exec_time) from ${TABLENAME} where task_status>-1"
${conn} -s -e "${select_sql}">${FILE_PATH}
IFS_OLD=${IFS}
IFS=$'n'
NOWTIME=$(date %s)
IS_ALARM=0
for line in `cat ${FILE_PATH}`
do
ID=$(echo ${line}|awk '{print $1}')
SEC=$(echo ${line}|awk '{print $2}')
TIME=$(echo ${line}|awk '{print $3}')
if [ ${TIME} -lt $[ ${NOWTIME}-1800 ] ];then//时间可以自由定
IS_ALARM=1
fi
done
IFS=${IFS_OLD}
if [ ${IS_ALARM} -eq 1 ];then
//告警
fi
4、脚本服务框架的本身是否运行的监控check_server.sh。
任务服务注意事项:
代码语言:javascript复制1、并发读取待执行任务,防止高并发时被读到同一条任务,利用MYSQL的DML的原子性保证。
2、拉待执行任务的时候,非超级任务,不可拉执行中的,超级任务才可以拉执行中,因为只有超级任务定义为,不需要等待上一
次执行完,才执行下一次。
3、结束单条任务的时候,同样需要注意超级任务与非超级任务的区别来决定 runnings 和 status 的状态。
这套脚本服务主要用于php执行业务相关逻辑,当然php也可以运行shell脚本,但不支持python等其它一些语言的脚本调用。并没有高大尚的技术,充分发挥了swoole php的性能,有更好的脚本框架的开发者,欢迎一起交流进步 ^ ^