工作项目中,传感器采集数据的存储是在每100毫秒的定时计划任务中完成的。这一步,是通过swoole中的tick方法实现的。
由于数据还同时往合作方系统的mq有发送,现在合作方感觉数据量太大,认为完全无此必要,设置成每隔几秒几分钟发送一次就好。
所以就诞生了本文要实现的任务:将定时任务的间隔时间,改成后台可以配置的方式。
这样一来,到底是要几秒,还是要几分钟,以后我们根据人家要求直接去后台改配置即可,就不用再去改代码。
本文翟码农主要分享实现思路,所以下面的代码可能有错误,敬请注意。
原本定时任务的实现大致如下:
$server = new swoole_websocket_server($ip,$port); $server->set([ 'reactor_num' => 4, 'worker_num' => 5 ]); $server->on('WorkerStart',function (swoole_websocket_server $server, $worker_id){ if(0 == $worker_id){ $server->tick(100, function(){...}); } }
现在要将tick方法里的第一个参数100ms改成后台可配置的方式。
如果将获取这个配置的代码,写在$server->tick这行代码的上面,那么要想让这个配置生效,就必须要在服务器里重启上面的服务进程。要求用户还要懂得去服务器里重启服务,这个肯定是不现实的。所以还必须得实现后台间隔时间的配置一旦修改,不用重启服务,也能立马改变定时计划任务的执行间隔时间。
翟码农想的是如下解决思路:
在定时计划任务的方法实现里加个状态变量,通过这个状态变量来确定这个任务方法要不要执行。而这个状态变量,再用一个间隔时间很短的定时计划任务,去根据后台配置的间隔时间,来确定这个状态变量的变化。
本文为翟码农个人博客蓝翟红尘里php分类下有关swoole定时计划任务动态改变间隔时间的原创文章,转载请注明出处:
http://www.zhai14.com/blog/how-to-set-the-interval-time-of-tick-function-in-swoole.html
下面就用简洁的php代码实现一下这个思路:
在上面服务里,再加一个定时计划任务,专门用来根据后台配置来设置定时任务需不需要执行的标志栏位。
$server->on('WorkerStart',function (swoole_websocket_server $server, $worker_id){ if(0 == $worker_id){ $server->tick(100, function(){...}); } if(1 == $worker_id){ $server->tick(100, '新添加的定时任务方法'); } }
这个额外添加的定时任务代码如下:
$redis_client = new \Redis($ip, $port); // 这里用数组,你就可以动态的控制多个定时任务的间隔时间了 $info_arr = [ [ // 存储下一个任务开始执行的时间 'key_end_time' => 'swoole_tick:end_time_func1', // 存储定时任务是否执行的判断标志 'key_do_flag' => 'swoole_tick:do_flag_func1', // 这个是后台配置的栏位 'key_interval' => 'interval_func1' ] ]; $now = time(); foreach( $info_arr as $item ){ if( !$redis_client->exists($item['key_do_flag']) ){ $interval = Config::getValue($item['key_interval]); $end_time = $now + $interval; $redis_client->set($item['key_end_time'], $end); $redis_client->set($item['key_do_flag'], 0); }else{ $end_time = $redis_client->get($item['key_end_time']); } if( $now > $end_time ){ $redis_client->set($item['key_do_flag'], 1); } }
然后我们一开始的那个定时计划任务方法实现,就可改成如下:
function taskForFunc1(){ $redis_client = Common::getRedisClient(); $do_flag = $redis_client->get('swoole_tick:do_flag_func1'); if( !$do_flag ){ return false; } //what u want me to do is here ... // 当上面没有return false时,此处一定要加if条件判断,不然定时间隔无效,仔细体会吧 if( 1 == $do_flag ){ //删除标志,通过另一个进程,开始生成下一次任务的开始时间 $redis_client->del('swoole_tick:do_flag_func1'); } }
上面代码里翟码农都是用的单进程,如果是多进程,任务方法里的代码就还得额外注意有关多进程方面的问题了。这个仅当提醒,本文就不继续拓展了。