背景
最近接到一个项目任务,需要实现一个商品的定时上下架的功能,且可以修改这个定时任务,还有默认销毁的机制存在,即如果对一个商品设置了定时上架的任务,在未执行前手动上架,则之前设置的定时任务自动失效,这对使用PHP来解决还是比较棘手的。
现状分析
一种可以使用暴力的方式,即对于请求的定时任务设置线程休眠等待,直到时间到了再执行,但一般php进程都不会常驻内存,执行完一个请求后就销毁了。这样做,无疑会造成内存的浪费,也要考虑PHP进程超时处理的策略,并且无法做到修改或者撤销定时任务的需求。
另一种是设置linux的系统定时任务,这个方式理论上是可行的,需要做到定时任务的相关的商品标识,是可以做到修改和删除指定的定时任务的,难点在于对系统定时任务的处理比较繁琐,且这个定时任务一般不能设置具体的执行逻辑,可以是访问一个php文件,这就意味着还得单独设处理逻辑。
思路
因为php基本都是用户触发机制的,因此我的思路是这个定时任务由用户来触发,每次有用户请求时,都去检查一次是否有未执行的定时任务,如果有则在执行完成后才返回结果,这样对于用户来说,定时任务是在生效中的。因为每次访问都要检查定时任务,为了不影响访问速度,我将相应的任务存放在redis缓存中,失效的任务也会及时销毁。
具体实现
//创建定时任务 public static function handleTimedTask($id, $action, $start_time, $manager, $manager_id) { $id = self::changeToArray($id); $time = time(); //从缓存中拿定时任务 $redis = new Redis(); $data = $redis->get(self::COMMODITY_TASK_LIST); if (!$data) { $data = array(); } foreach ($id as $value) { //删除任务 if ($action != 0 && $action != 1) { unset($data[$value]); } //更新任务 else { if ($start_time < $time) { return 0; } $one = array(); $one[self::ACTION] = $action; $one[self::START_TIME] = $start_time; $one[Commodity::MANAGER] = $manager; $one[Commodity::MANAGER_ID] = $manager_id; $data[$value] = $one; } } //重新存储 $redis->set(self::COMMODITY_TASK_LIST, json_encode($data)); return 0; } //检查是否有要执行的定时任务 public static function checkTimedTask() { $time = time(); //从缓存中拿定时任务 $redis = new Redis(); $data = $redis->get(self::COMMODITY_TASK_LIST); if (!$data) { return []; } foreach ($data as $key => $value) { if ($value[self::START_TIME] <= $time) { //执行任务 //... //if ($ret < 0) return $ret; //删除任务 unset($data[$key]); } } //重新存储 $redis->set(self::COMMODITY_TASK_LIST, json_encode($data)); return $data; }
redis配置持久化
因为上面设置的定时任务是存储在redis中的,redis的数据是放在内存中的,如果一旦发生意外退出的情况,数据就会丢失,因此需要做好持久化,redis提供了两种持久化的方式:RDB和AOF。
1. RDB
redis中是默认配置的持久化方式RDB的方式
save 900 1 save 300 10 save 60 10000
配置含义:
900秒内,如果超过1个key被修改,则发起快照保存
300秒内,如果超过10个key被修改,则发起快照保存
60秒内,如果1万个key被修改,则发起快照保存
2.AOF(推荐)
redis.conf默认配置:
appendonly no
配置文件中的appendonly修改为yes,开启AOF持久化。开启后,启动redis服务端,发现多了一个appendonly.aof文件。
使用AOF做持久化,每一个命令以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写,使得 AOF文件的体积不会超出保存数据集状态所需的实际大小。实际上,AOF持久化并不会立即将命令写入到硬盘文件中,而是写入到硬盘缓存,在接下来的策略中,配置多久来从硬盘缓存写入到硬盘文件。所以在一定程度一定条件下,还是会有数据丢失,不过你可以大大减少数据损失
建议用AOF的方式来做持久化