文章大纲

swoole毫秒级定时任务的间隔时间,如何通过后台配置动态生效而不用重启服务

2020-06-13 23:35:10

工作项目中,传感器采集数据的存储是在每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');
    }
}


上面代码里翟码农都是用的单进程,如果是多进程,任务方法里的代码就还得额外注意有关多进程方面的问题了。这个仅当提醒,本文就不继续拓展了。




我要评论
评论列表