页面交互效果

下面是写好的示例前端交互页面,主要是列表页面,编辑页面。

主要交互有:

1 开启定时任务进程

2 关闭定时任务进程

3 新增一项定时任务

4 编辑已有的定时任务

5 删除定时任务

6 开始一项定时任务

7 停止一项定时任务

定时任务分两种:

1 执行一次

2 循环执行

定时任务执行的内容:

定时任务执行的内容其实还是一个http请求。 通过定时的方式,指定时间执行或者循执行。

1 列表页面

2 新建\编辑页面

代码地址,演示地址

示例代码是基于Laravel,Alpaca-spa框架编写,并且作为 ‘Alpaca-Spa-Laravel后台管理平台’的一个模块儿集成于系统中。

内容 说明 地址
Alpaca-Spa 主页 http://www.tkc8.com
Alpaca-Spa-Laravel 后台管理端 http://full.tkc8.com
Alpaca-Spa-Sui 手机端sui http://full.tkc8.com/app
oschina 代码 http://git.oschina.net/cc-sponge/Alpaca-Spa-Laravel
github 代码 https://github.com/big-sponge/Alpaca-Spa-Laravel

适用范围

定时精确时间不低于1秒。web服务重启、或者php重启。该定时任务不会自动重启。

实现原理

用PHP实现定时任务(非linux-shell方式,与操作系统平台无关),主要两个技术点:

1)PHP后台进程

2)PHP异步处理

1 PHP后台进程

一般情况,用命令行可以开启一个php后台进程。而在浏览器中通过HTTP请求一个php处理,会因为浏览器关闭,或者请求超时,使得后台的php处理中断。但是php提供了两个方法可以会忽略浏览器关闭、请求超时:


  1. ignore_user_abort(true); // 忽略客户端断开
  2. set_time_limit(0); // 设置执行不超时

因此,我们可以使用这两个函数从浏览器以HTTP请求的方式开启一个php后台进程。

2 PHP异步处理

php语言本身没有可以跨平台好用异步处理方法,但是可以通过curl或者fsockopen创建一个请求来实现异步处理。这里我们用fsockopen方法实现:


  1. $fp = fsockopen("$ip", $port, $errno, $errstr,1);
  2. if (!$fp) {
  3. return 'worker error:'."$errstr ($errno)<br />\n";
  4. } else {
  5. $out = "POST $url HTTP/1.1\r\n";
  6. $out .= "Host: $ip\r\n";
  7. $out .= "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n";
  8. $out .= "Content-Length: " . strlen($postData) . "\r\n";
  9. $out .= "Connection: close\r\n";
  10. $out .="\r\n";
  11. $out .=$postData;
  12. fputs($fp, $out);
  13. fclose($fp);
  14. }

前台交互控制器

为了在前端界面方便控制定时任务的开启关闭,新增、编辑、删除,查看执行状态。 同时也可以添加权限控制。 主要有8个操作接口(下面是以Laravel 路由示例):

1 查看定时任务进程状态

2 开始定时任务进程

3 停止定时任务进程

4 添加,或者编辑定时任务

5 设置定时任务状态

6 获取指定定时任务明细

7 删除定时任务

8 获取定时任务列表


  1. /* crontab - status 查看定时任务守护进程状态 */
  2. Route::any('crontab/status', "CrontabController@status");
  3. /* crontab - start 开始定时任务 */
  4. Route::any('crontab/start', "CrontabController@start");
  5. /* crontab - stop 停止定时任务守护进程*/
  6. Route::any('crontab/stop', "CrontabController@stop");
  7. /* crontab - editTask 添加,或者编辑定时任务*/
  8. Route::any('crontab/editTask', "CrontabController@editTask");
  9. /* crontab - changeTaskStatus 设置定时任务状态 */
  10. Route::any('crontab/changeTaskStatus', "CrontabController@changeTaskStatus");
  11. /* crontab - getIndexTask 获取指定定时任务 */
  12. Route::any('crontab/getIndexTask', "CrontabController@getIndexTask");
  13. /* crontab - removeTask 删除定时任务 */
  14. Route::any('crontab/removeTask', "CrontabController@removeTask");
  15. /* crontab - listTask 获取定时任务列表 */
  16. Route::any('crontab/listTask', "CrontabController@listTask");

完整的类代码如下:


  1. <?php
  2. namespace App\Modules\Manage\Controllers;
  3. use Crontab\Library\Crontab\AlpacaCrontab;
  4. use Crontab\Library\Crontab\AlpacaDaemon;
  5. use Crontab\Library\Crontab\AlpacaWorker;
  6. use App\Modules\Manage\Controllers\Base\BaseController;
  7. use App\Common\Code;
  8. use App\Common\Msg;
  9. /**
  10. * 定时任务管理控制器
  11. * @author Chengcheng
  12. * @date 2016-10-19 15:50:00
  13. */
  14. class CrontabController extends BaseController
  15. {
  16. /**
  17. * 设置不需要登录的的Action,不加Action前缀
  18. * @author Chengcheng
  19. * @date 2016年10月23日 20:39:25
  20. * @return array
  21. */
  22. protected function noLogin()
  23. {
  24. return [];
  25. }
  26. /**
  27. * 设置不需要权限验证的Action,不加Action前缀
  28. * @author Chengcheng
  29. * @date 2016年10月23日 20:39:25
  30. * @return array
  31. */
  32. protected function noAuth()
  33. {
  34. // 以下Action不需要角色权限
  35. return [];
  36. }
  37. /**
  38. * 查看定时任务守护进程状态
  39. * @author Chengcheng
  40. * @date 2016-10-23 20:34:00
  41. */
  42. public function status()
  43. {
  44. //查看守护进程状态
  45. $result['code'] = Code::SYSTEM_OK;
  46. $result['msg'] = Msg::SYSTEM_OK;
  47. $result['data'] = AlpacaDaemon::daemon()->status();
  48. //返回结果
  49. return $this->ajaxReturn($result);
  50. }
  51. /**
  52. * 开始定时任务
  53. * @author Chengcheng
  54. * @date 2016-10-23 20:34:00
  55. */
  56. public function start()
  57. {
  58. //异步开启守护进程
  59. $result['code'] = Code::SYSTEM_OK;
  60. $result['msg'] = Msg::SYSTEM_OK;
  61. $result['data'] = AlpacaWorker::worker()->action(['REQUEST_URI' => "/crontab/index/start"]);
  62. //返回结果
  63. return $this->ajaxReturn($result);
  64. }
  65. /**
  66. * 停止定时任务守护进程
  67. * @author Chengcheng
  68. * @date 2016-10-23 20:34:00
  69. */
  70. public function stop()
  71. {
  72. //停止守护进程
  73. $result['code'] = Code::SYSTEM_OK;
  74. $result['msg'] = Msg::SYSTEM_OK;
  75. $result['data'] = AlpacaDaemon::daemon()->stop();
  76. //返回结果
  77. return $this->ajaxReturn($result);
  78. }
  79. /**
  80. * 添加,或者编辑定时任务
  81. * @author Chengcheng
  82. * @date 2016-10-23 20:34:00
  83. */
  84. public function editTask()
  85. {
  86. /*
  87. * 1 获取输入参数
  88. * BEGIN_TIME 开始时间
  89. * END_TIME 结束时间
  90. * INTERVAL 时间间隔
  91. * NAME 名称
  92. * STATUS 状态 1-ENABLED, 2-DISABLE
  93. * TASK_TYPE 类型 1-ONCE, 2-LOOP
  94. * ACTION 要执行的Action
  95. * INDEX 索引,null或者0时候,表示新建
  96. * */
  97. $this->requestData['NAME'] = $this->input('NAME', null);
  98. $this->requestData['BEGIN_TIME'] = $this->input('BEGIN_TIME', null);
  99. $this->requestData['END_TIME'] = $this->input('END_TIME', null);
  100. $this->requestData['INTERVAL'] = $this->input('INTERVAL', null);
  101. $this->requestData['TASK_TYPE'] = $this->input('TASK_TYPE', '1');
  102. $this->requestData['ACTION'] = $this->input('ACTION', null);
  103. $this->requestData['STATUS'] = $this->input('STATUS', '2');
  104. $this->requestData['INDEX'] = $this->input('INDEX', null);
  105. $this->requestData['LAST_TIME'] = $this->input('LAST_TIME', null);
  106. //2 检查参数
  107. if (empty($this->requestData['BEGIN_TIME'])) {
  108. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  109. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'BEGIN_TIME');
  110. return $this->ajaxReturn($result);
  111. }
  112. if ($this->requestData['TASK_TYPE'] == 2 && empty($this->requestData['END_TIME'])) {
  113. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  114. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'END_TIME');
  115. return $this->ajaxReturn($result);
  116. }
  117. if (empty($this->requestData['ACTION'])) {
  118. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  119. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'ACTION');
  120. return $this->ajaxReturn($result);
  121. }
  122. if ($this->requestData['TASK_TYPE'] == 2 && empty($this->requestData['INTERVAL'])) {
  123. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  124. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INTERVAL');
  125. return $this->ajaxReturn($result);
  126. }
  127. //3 设置结束时间
  128. $now = date('Y-m-d H:i:s', time());
  129. $nextTime = date('Y-m-d H:i:s', strtotime($this->requestData['INTERVAL'], strtotime($this->requestData['BEGIN_TIME'])));
  130. if ($this->requestData['TASK_TYPE'] == "1" || strtotime($now) < strtotime($this->requestData['BEGIN_TIME'])) {
  131. $nextTime = $this->requestData['BEGIN_TIME'];
  132. }
  133. //4 创建任务
  134. $task = array(
  135. 'NAME' => $this->requestData['NAME'], //NAME
  136. 'STATUS' => $this->requestData['STATUS'], // 1-ENABLED, 2-DISABLE
  137. 'TYPE' => $this->requestData['TASK_TYPE'], // 1-ONCE, 2-LOOP
  138. 'INTERVAL' => $this->requestData['INTERVAL'], //year(年),month(月),hour(小时)minute(分),second(秒)
  139. 'BEGIN_TIME' => $this->requestData['BEGIN_TIME'], //开始时间
  140. 'NEXT_TIME' => $nextTime, //下次执行时间
  141. 'LAST_TIME' => $this->requestData['LAST_TIME'], //上次执行时间
  142. 'ACTION' => $this->requestData['ACTION'], //执行的ACTION
  143. 'END_TIME' => $this->requestData['END_TIME'], //截止时间2
  144. );
  145. //5 判断是新建还是修改
  146. if (empty($this->requestData['INDEX'])) {
  147. //新建
  148. $info = AlpacaCrontab::crontab()->addTask($task);
  149. } else {
  150. $this->requestData['INDEX'] -= 1;
  151. $info = AlpacaCrontab::crontab()->editTask($this->requestData['INDEX'], $task);
  152. }
  153. //5 返回结果
  154. $result['code'] = Code::SYSTEM_OK;
  155. $result['msg'] = Msg::SYSTEM_OK;
  156. $result['data'] = $info;
  157. return $this->ajaxReturn($result);
  158. }
  159. /**
  160. * 设置定时任务状态
  161. * @author Chengcheng
  162. * @date 2016-10-23 20:34:00
  163. */
  164. public function changeTaskStatus()
  165. {
  166. /*
  167. * 1 获取输入参数
  168. * STATUS 状态 1-ENABLED, 2-DISABLE
  169. * INDEX 索引
  170. * */
  171. $this->requestData['STATUS'] = $this->input('STATUS', '2');
  172. $this->requestData['INDEX'] = $this->input('INDEX', null);
  173. //2 检查参数
  174. if (empty($this->requestData['STATUS'])) {
  175. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  176. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'STATUS');
  177. return $this->ajaxReturn($result);
  178. }
  179. if (empty($this->requestData['INDEX'])) {
  180. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  181. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INDEX');
  182. return $this->ajaxReturn($result);
  183. }
  184. //3 修改状态
  185. $this->requestData['INDEX'] -= 1;
  186. $data = AlpacaCrontab::crontab()->editTaskStatus($this->requestData['INDEX'], $this->requestData['STATUS']);
  187. //4 返回结果
  188. $result['code'] = Code::SYSTEM_OK;
  189. $result['msg'] = Msg::SYSTEM_OK;
  190. $result['data'] = $data;
  191. return $this->ajaxReturn($result);
  192. }
  193. /**
  194. * 查找单条定时任务
  195. * @author Chengcheng
  196. * @date 2016-10-23 20:34:00
  197. */
  198. public function getIndexTask()
  199. {
  200. /*
  201. * 1 获取输入参数
  202. * INDEX 索引
  203. * */
  204. $this->requestData['INDEX'] = $this->input('INDEX', null);
  205. //2 检查参数
  206. if (empty($this->requestData['INDEX'])) {
  207. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  208. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INDEX');
  209. return $this->ajaxReturn($result);
  210. }
  211. //3 删除
  212. $this->requestData['INDEX'] -= 1;
  213. $data = AlpacaCrontab::crontab()->getIndexTask($this->requestData['INDEX']);
  214. //4 返回结果
  215. $result['code'] = Code::SYSTEM_OK;
  216. $result['msg'] = Msg::SYSTEM_OK;
  217. $result['data'] = $data;
  218. return $this->ajaxReturn($result);
  219. }
  220. /**
  221. * 删除定时任务
  222. * @author Chengcheng
  223. * @date 2016-10-23 20:34:00
  224. */
  225. public function removeTask()
  226. {
  227. /*
  228. * 1 获取输入参数
  229. * INDEX 索引
  230. * */
  231. $this->requestData['INDEX'] = $this->input('INDEX', null);
  232. //2 检查参数
  233. if (empty($this->requestData['INDEX'])) {
  234. $result["code"] = Code::SYSTEM_PARAMETER_NULL;
  235. $result["msg"] = sprintf(Msg::SYSTEM_PARAMETER_NULL, 'INDEX');
  236. return $this->ajaxReturn($result);
  237. }
  238. //3 删除
  239. $this->requestData['INDEX'] -= 1;
  240. $data = AlpacaCrontab::crontab()->removeTask($this->requestData['INDEX']);
  241. //4 返回结果
  242. $result['code'] = Code::SYSTEM_OK;
  243. $result['msg'] = Msg::SYSTEM_OK;
  244. $result['data'] = $data;
  245. return $this->ajaxReturn($result);
  246. }
  247. /**
  248. * 查看定时任务列表
  249. * @author Chengcheng
  250. * @date 2016-10-23 20:34:00
  251. */
  252. public function listTask()
  253. {
  254. //查找
  255. $data['task'] = AlpacaCrontab::crontab()->listTask();
  256. $data['total'] = count($data['task']);
  257. $data['status'] = AlpacaDaemon::daemon()->status();
  258. //返回结果
  259. $result['code'] = Code::SYSTEM_OK;
  260. $result['msg'] = Msg::SYSTEM_OK;
  261. $result['data'] = $data;
  262. return $this->ajaxReturn($result);
  263. }
  264. }

实现后台进程类

使用 ignore_user_abort(true); set_time_limit(0); 可以从浏览器开启一个php后台进程。为了避免出现多个后台进程,需要借助一个配置来标识该后台进程是否已经启动。


  1. {"code":"1001","message":"Stop at:2017-02-24 11:29:43"}

当code是1001时候,表示后台进程未启动,这时通过http请求开启后台进程时,正常启动

当code是1000时候,表示后台进程已经启动,这时通过http请求开启后台进程时,不做任何操作,以为进程已经启动

当开启后台进程的请求到达后台时候,读取配置文件,如果code是1001,则启动进程,并且设置code为1000,保存配置文件。

当关闭后台进程的请求到达后台时候,读取配置文件,设置code为1001,保存配置文件。

后台进程在运行时候,每隔一秒读取配置文件,判断code状态,如果是1001,则结束执行; 如果是1000,则继续执行

完整类的代码如下:


  1. <?php
  2. namespace Crontab\Library\Crontab;
  3. /**
  4. * 守护进程
  5. * @author Chengcheng
  6. * @date 2016年10月21日 17:04:44
  7. */
  8. class AlpacaDaemon
  9. {
  10. private $daemon_json = __DIR__ . '/deamon.json';
  11. private static $instance;
  12. private $events = [];
  13. public static function daemon()
  14. {
  15. return self::getInstance();
  16. }
  17. private static function getInstance()
  18. {
  19. if(!self::$instance){
  20. self::$instance = new self();
  21. self::$instance->daemon_json = base_path('storage') . '/crontab/deamon.json';
  22. }
  23. return self::$instance;
  24. }
  25. public function setDaemon($daemon_json)
  26. {
  27. $this->daemon_json = $daemon_json;
  28. return $this;
  29. }
  30. public function setEvents(array $events)
  31. {
  32. $this->events = $events;
  33. return $this;
  34. }
  35. public function status()
  36. {
  37. $data = json_decode(file_get_contents($this->daemon_json),true);
  38. if(empty($data)){
  39. $data = array();
  40. }
  41. return $data;
  42. }
  43. public function stop()
  44. {
  45. $data =new \stdClass();
  46. $data->code="1001";
  47. $data->message="Stop at:".date("Y-m-d H:i:s" ,time());
  48. file_put_contents($this->daemon_json,json_encode($data),LOCK_EX);
  49. $result["result_code"] = "1";
  50. $result["result_message"] = "操作成功";
  51. return $result;
  52. }
  53. public function start()
  54. {
  55. $data = json_decode(file_get_contents($this->daemon_json) , true);
  56. if(empty($data)){
  57. $data['code']="1001";
  58. }
  59. if($data['code'] == "1000" ){
  60. //die("Error - exit, Already running !");
  61. return;
  62. }
  63. $data['code']="1000";
  64. $data['message']="Start";
  65. file_put_contents($this->daemon_json,json_encode($data),LOCK_EX);
  66. ignore_user_abort(true); // 忽略客户端断开
  67. set_time_limit(0); // 设置执行不超时
  68. while(true){
  69. $data = json_decode(file_get_contents($this->daemon_json) , true);
  70. if(empty($data) || empty($data['code']) || $data['code'] == "1001" ){
  71. break;
  72. }
  73. if(!empty($this->events)){
  74. foreach ($this->events as $e){
  75. $e();
  76. }
  77. }
  78. $data['message'] = date("Y-m-d H:i:s" ,time())." : Working ...";
  79. file_put_contents($this->daemon_json, json_encode($data), LOCK_EX);
  80. sleep(1);
  81. }
  82. $this->stop();
  83. }
  84. }

实现异步处理的类

完整类的代码如下:

  1. <?php
  2. namespace Crontab\Library\Crontab;
  3. class AlpacaWorker
  4. {
  5. private static $instance;
  6. private $accessToken = '';
  7. public static function worker()
  8. {
  9. return self::getInstance();
  10. }
  11. private static function getInstance()
  12. {
  13. if(!self::$instance){
  14. self::$instance = new self();
  15. self::$instance->accessToken= 'VyKfohBbwlkTOqp2jvIWPW92';
  16. }
  17. return self::$instance;
  18. }
  19. public function action(array $worker = null)
  20. {
  21. //获取参数
  22. $ip = empty($worker['SERVER_ADDR']) ? $_SERVER['SERVER_NAME'] : $worker['SERVER_ADDR']; //服务器IP地址
  23. $port = empty($worker['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $worker['SERVER_PORT']; //服务器端口
  24. $url = empty($worker['REQUEST_URI']) ? '/' :$worker['REQUEST_URI']; //服务器URL
  25. $data = empty($worker['REQUEST_DATA']) ? '' :$worker['REQUEST_DATA']; //请求参数
  26. //格式化请求参数
  27. $postData = "";
  28. $needChar = false;
  29. if(is_array($data)){
  30. foreach($data as $key => $val) {
  31. $postData .= ($needChar ? "&" : "") . urlencode($key) . "=" . urlencode($val);
  32. $needChar = true;
  33. }
  34. }else{
  35. $postData = $data;
  36. }
  37. $url=$url."?accessToken=".$this->accessToken;
  38. //使用fsockopen方式异步调用action
  39. $fp = fsockopen("$ip", $port, $errno, $errstr,1);
  40. if (!$fp) {
  41. return 'worker error:'."$errstr ($errno)<br />\n";
  42. } else {
  43. $out = "POST $url HTTP/1.1\r\n";
  44. $out .= "Host: $ip\r\n";
  45. $out .= "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n";
  46. $out .= "Content-Length: " . strlen($postData) . "\r\n";
  47. $out .= "Connection: close\r\n";
  48. $out .="\r\n";
  49. $out .=$postData;
  50. fputs($fp, $out);
  51. fclose($fp);
  52. }
  53. return 'worker success!';
  54. }
  55. }

定时任务处理类

定时任务处理类 主要是实现新增、编辑、删除定时任务,执行定时任务要处理的方法

定时任务的信息以json格式存放在下面的配置文件中

  1. [{"NAME":"\u6d4b\u8bd5\u5b9a\u65f6\u4efb\u52a12","STATUS":"2","TYPE":"1","INTERVAL":"5 second","BEGIN_TIME":"2017-02-21 11:55:00","NEXT_TIME":"2017-02-21 11:55:00","LAST_TIME":null,"ACTION":"\/main\/crontab\/index2","END_TIME":"2017-02-10 15:55:00"},{"NAME":"TEST - log","STATUS":"2","TYPE":"2","INTERVAL":"5 second","BEGIN_TIME":"2017-08-10 09:00:53","NEXT_TIME":"2017-08-10 09:59:00","LAST_TIME":"2017-08-10 09:58:55","ACTION":"\/crontab\/task\/test","END_TIME":"2017-08-11 09:25:53"}]

主要的字段为:

INDEX 索引

BEGIN_TIME 开始时间

END_TIME 结束时间

INTERVAL 时间间隔

NAME 名称

STATUS 状态 1-ENABLED, 2-DISABLE

TASK_TYPE 类型 1-ONCE, 2-LOOP

ACTION 要执行的Action

类中主要有7个方法:

listTask() 查看定时任务列表

addTask() 添加定时任务

editTask() 编辑定时任务

editTaskStatus() 编辑定时任务状态

getIndexTask() 获取指定定时任务信息

removeTask() 删除定时任务

doTask() 执行定时任务指定的任务

完整类的代码如下:

  1. <?php
  2. namespace Crontab\Library\Crontab;
  3. /**
  4. * 定时任务
  5. * @author Chengcheng
  6. * @date 2016年10月21日 17:04:44
  7. */
  8. class AlpacaCrontab
  9. {
  10. //定时任务文件
  11. private $task_json = __DIR__ .'/crontab.json';
  12. //单例
  13. private static $instance;
  14. //单例
  15. public static function crontab()
  16. {
  17. return self::getInstance();
  18. }
  19. //单例
  20. private static function getInstance()
  21. {
  22. if(!self::$instance){
  23. self::$instance = new self();
  24. self::$instance->task_json = base_path('storage') . '/crontab/crontab.json';
  25. }
  26. return self::$instance;
  27. }
  28. /**
  29. * 配置
  30. * @author Chengcheng
  31. * @param array $crontab
  32. * @date 2016-10-23 20:34:00
  33. * @return array
  34. */
  35. public function setConfig($crontab)
  36. {
  37. $this->task_json = $crontab;
  38. return $this;
  39. }
  40. /**
  41. * 查看定时任务
  42. * @author Chengcheng
  43. * @date 2016-10-23 20:34:00
  44. * @return array
  45. */
  46. public function listTask()
  47. {
  48. $tasks = json_decode(file_get_contents($this->task_json));
  49. $i = 0;
  50. foreach ($tasks as $task)
  51. {
  52. $tasks[$i]->INTERVAL = $this->timeToStr($tasks[$i]->INTERVAL);
  53. $i++;
  54. }
  55. return $tasks;
  56. }
  57. /**
  58. * 添加定时任务
  59. * @author Chengcheng
  60. * @date 2016-10-23 20:34:00
  61. * @return array
  62. */
  63. public function addTask($task)
  64. {
  65. $result["result_code"] = "1";
  66. $result["result_message"] = "添加成功";
  67. $tasks = json_decode(file_get_contents($this->task_json),true);
  68. $tasks[count($tasks)] = $task;
  69. file_put_contents($this->task_json, json_encode($tasks), LOCK_EX);
  70. return $result;
  71. }
  72. /**
  73. * 编辑定时任务
  74. * @author Chengcheng
  75. * @param string $index
  76. * @param string $task
  77. * @date 2016-10-23 20:34:00
  78. * @return array
  79. */
  80. public function editTask($index,$task)
  81. {
  82. $result["result_code"] = "1";
  83. $result["result_message"] = "修改成功";
  84. $tasks = json_decode(file_get_contents($this->task_json));
  85. $tasks[$index] = $task;
  86. file_put_contents($this->task_json, json_encode($tasks), LOCK_EX);
  87. return $result;
  88. }
  89. /**
  90. * 编辑定时任务状态
  91. * @author Chengcheng
  92. * @param string $index
  93. * @param string $status
  94. * @date 2016-10-23 20:34:00
  95. * @return array
  96. */
  97. public function editTaskStatus($index,$status)
  98. {
  99. $result_data["result_code"] = "1";
  100. $result_data["result_message"] = "修改状态成功[".$status."]";
  101. $tasks = json_decode(file_get_contents($this->task_json));
  102. $tasks[$index]->STATUS = $status;
  103. file_put_contents($this->task_json, json_encode($tasks), LOCK_EX);
  104. return $result_data;
  105. }
  106. /**
  107. * 获取定时任务
  108. * @author Chengcheng
  109. * @param string $index
  110. * @date 2016-10-23 20:34:00
  111. * @return array
  112. */
  113. public function getIndexTask($index)
  114. {
  115. $result_data["result_code"] = "1";
  116. $result_data["result_message"] = "获取任务成功【".$index."】";
  117. $tasks = json_decode(file_get_contents($this->task_json));
  118. $result_data["result_data"] = $tasks[$index];
  119. return $result_data;
  120. }
  121. /**
  122. * 删除定时任务
  123. * @author Chengcheng
  124. * @param string $index
  125. * @date 2016-10-23 20:34:00
  126. * @return array
  127. */
  128. public function removeTask($index)
  129. {
  130. $result_data["result_code"] = "1";
  131. $result_data["result_message"] = "删除任务【".$index."】成功";
  132. $tasks = json_decode(file_get_contents($this->task_json));
  133. array_splice($tasks, $index, 1);
  134. file_put_contents($this->task_json, json_encode($tasks), LOCK_EX);
  135. return $result_data;
  136. }
  137. /**
  138. * 执行定时任务
  139. * @author Chengcheng
  140. * @date 2016-10-23 20:34:00
  141. * @return array
  142. */
  143. public function doTask()
  144. {
  145. $tasks = json_decode(file_get_contents($this->task_json) ,true);
  146. if(empty($tasks)){ return ;}
  147. $now = date('Y-m-d H:i:s',time());
  148. foreach ($tasks as &$task){
  149. if(empty($task['STATUS']) || empty($task['TYPE']) || empty($task['BEGIN_TIME']) || empty($task['ACTION']) )
  150. {
  151. continue;
  152. }
  153. if($task['STATUS'] != 1)
  154. {
  155. continue;
  156. }
  157. if(!empty($task['END_TIME']) && strtotime($now)>=strtotime($task['END_TIME'])){
  158. $task['NEXT_TIME']='END';
  159. continue;
  160. }
  161. if($task['TYPE'] == 1 && empty($task['NEXT_TIME']) )
  162. {
  163. continue;
  164. }
  165. if($task['TYPE'] == 2 && empty($task['INTERVAL']) )
  166. {
  167. continue;
  168. }
  169. if(!empty($task['NEXT_TIME']) && $task['NEXT_TIME']=='END' )
  170. {
  171. continue;
  172. }
  173. if($task['TYPE'] == 1 && (strtotime($now)>=strtotime($task['NEXT_TIME'])))
  174. {
  175. $task['LAST_TIME']= $now;
  176. $task['NEXT_TIME']='END';
  177. $task['STATUS']=2;
  178. AlpacaWorker::worker()->action(['REQUEST_URI'=>"{$task['ACTION']}"]);
  179. continue;
  180. }
  181. if($task['TYPE'] == 2)
  182. {
  183. if(empty($task['NEXT_TIME'])){
  184. $task['NEXT_TIME'] = $task['BEGIN_TIME'];
  185. }
  186. if(strtotime($now)>=strtotime($task['NEXT_TIME'])){
  187. $task['LAST_TIME']= $now;
  188. $task['NEXT_TIME']= date('Y-m-d H:i:s',strtotime($task['INTERVAL']));
  189. AlpacaWorker::worker()->action(['REQUEST_URI'=>"{$task['ACTION']}"]);
  190. }
  191. continue;
  192. }
  193. }
  194. file_put_contents($this->task_json, json_encode($tasks), LOCK_EX);
  195. return $tasks;
  196. }
  197. /**
  198. * 格式化时间
  199. * @author Chengcheng
  200. * @param string $interval
  201. * @date 2016-10-23 20:34:00
  202. * @return array
  203. */
  204. private function timeToStr($interval)
  205. {
  206. $result = "";
  207. if($interval != null && $interval != ""){
  208. $temp = explode(" ", $interval);
  209. $iNumTemp = $temp[0];
  210. $iType = $temp[1];
  211. $iNum = str_replace("+", "", $iNumTemp);
  212. $str = "";
  213. switch ($iType){
  214. case "year":
  215. $str = "(年)";
  216. break;
  217. case "month":
  218. $str = "(月)";
  219. break;
  220. case "day":
  221. $str = "(日)";
  222. break;
  223. case "hour":
  224. $str = "(小时)";
  225. break;
  226. case "minute":
  227. $str = "(分)";
  228. break;
  229. case "second":
  230. $str = "(秒)";
  231. break;
  232. default:
  233. break;
  234. }
  235. $result = $iNum. $str;
  236. }
  237. return $result;
  238. }
  239. }

定时任务进程入口控制器

主要是为了实现异步开启后台进程

  1. <?php
  2. namespace Crontab\Controllers;
  3. use Crontab\Common\Code;
  4. use Crontab\Common\Msg;
  5. use Crontab\Controllers\Base\BaseController;
  6. use Crontab\Library\Crontab\AlpacaCrontab;
  7. use Crontab\Library\Crontab\AlpacaDaemon;
  8. use Crontab\Library\Crontab\AlpacaWorker;
  9. /**
  10. * index
  11. * @author Chengcheng
  12. * @date 2017-02-22 15:50:00
  13. */
  14. class IndexController extends BaseController
  15. {
  16. /**
  17. * 设置不需要登录的的Action,不加Action前缀
  18. * @author Chengcheng
  19. * @date 2016年10月23日 20:39:25
  20. * @return array
  21. */
  22. protected function withoutLoginActions()
  23. {
  24. }
  25. /**
  26. * 开始定时任务的守护进程
  27. * @author Chengcheng
  28. * @date 2016-10-23 20:34:00
  29. */
  30. public function start()
  31. {
  32. //开始守护进程
  33. $result['code'] = Code::SYSTEM_OK;
  34. $result['msg'] = Msg::SYSTEM_OK;
  35. //在守护进程中注入定时任务
  36. $events = ['0'=>function(){
  37. AlpacaWorker::worker()->action(['REQUEST_URI'=>"/crontab/index/task"]);
  38. }];
  39. AlpacaDaemon::daemon()->setEvents($events);
  40. AlpacaDaemon::daemon()->start();
  41. //返回结果
  42. return $this->ajaxReturn($result);
  43. }
  44. /**
  45. * 停止定时任务的守护进程
  46. * @author Chengcheng
  47. * @date 2016-10-23 20:34:00
  48. */
  49. public function stop()
  50. {
  51. //停止守护进程
  52. $result['code'] = Code::SYSTEM_OK;
  53. $result['msg'] = Msg::SYSTEM_OK;
  54. $result['data'] = AlpacaDaemon::daemon()->stop();
  55. //返回结果
  56. return $this->ajaxReturn($result);
  57. }
  58. /**
  59. * 执行定时任务
  60. * @author Chengcheng
  61. * @date 2016-10-23 20:34:00
  62. */
  63. public function task()
  64. {
  65. //执行定时任务
  66. $result['code'] = Code::SYSTEM_OK;
  67. $result['msg'] = Msg::SYSTEM_OK;
  68. $result['data'] = AlpacaCrontab::crontab()->doTask();
  69. //返回结果
  70. return $this->ajaxReturn($result);
  71. }
  72. }

以上是PHP实现定时任务的核心类与方法, 完整的代码请参看代码服务器中提供的源码。

欢迎讨论

作者: Sponge

邮箱: 1796512918@qq.com

原文地址:https://www.jianshu.com/p/c68dba7736eb

PHP实现定时任务(非linux-shell方式,与操作系统无关)的更多相关文章

  1. linux shell 自动判断操作系统release 然后连接FTP yum源的脚本

    如何搭建本地yum源见附录① 如何搭建FTP yum源见附录② 脚本正文: #!/bin/sh# CenterOS config yumOSV=`rpm -q --qf %{version} cent ...

  2. linux shell执行方式

    linux shell执行有两种方式 shell脚本以#!/bin/bash开头,执行shell时先检查首行,在内部以下列方式执行: $/bin/bash script.sh 1. 使用sh执行. $ ...

  3. 《学渣Linux笔记》——关于.bashrc与profile(涉及交互式与非交互式、登录与非登录shell)

    <学渣Linux笔记>--关于.bashrc与profile(涉及交互式与非交互式.登录与非登录shell) 1.基本概念(个人理解) 交互式shell:等待用户输入,并执行相应操作的sh ...

  4. Linux - Shell - shell 执行方式

    概述 shell 的执行方式 背景 偶尔执行个 shell 脚本 一般都用 './script' 执行 最近忽然看到 有不同的执行方式, 感觉有必要整理一下, 然后和大家分享 准备 os centos ...

  5. linux,windows下检测指定的IP地址是否可用或者检测IP地址冲突的3种方式(批处理程序,python程序,linux shell 批量ping)

    本文中的脚本适用范围: 1)检测某些IP地址是否被占用: 2)检测网络中某些设备是否存活: 3)在分配新的ip地址之前,批量检测环境中是否存在冲突的机器 以上检测基于ICMP Ping报文,要求所有的 ...

  6. 【奇技淫巧】linux 定时任务 crontab 反弹 shell

    日期:2018-11-26 13:47:34 介绍:如何使用定时任务来反弹 shell? 0x01. 基本命令 参数 -e:编辑该用户的计时器设置: -l:列出该用户的计时器设置: -r:删除该用户的 ...

  7. linux shell & bash

    shell & bash shell指允许用户通过文本操作计算机的程序. interactive shell:从是否通过标准输入输出与用户进行交互的角度分为交互式shell(interacti ...

  8. Linux-(3)Linux Shell 使用

    三.Linux Shell 3.1 文件管理 3.1.1 ls 命令 显示指定工作目录下的内容及属性信息 ls 命令是Linux下最常用的指令之一.ls命令为英文单词 list 的缩写,正如英文单词 ...

  9. Linux shell脚本编程(一)

    Linux shell脚本编程: 守护进程,服务进程:启动?开机时自动启动: 交互式进程:shell应用程序 广义:GUI,CLI GUI: CLI: 词法分析:命令,选项,参数 内建命令: 外部命令 ...

  10. Linux Shell 文本处理工具集锦 zz

    内容目录: find 文件查找 grep 文本搜索 xargs 命令行参数转换 sort 排序 uniq 消除重复行 用tr进行转换 cut 按列切分文本 paste 按列拼接文本 wc 统计行和字符 ...

随机推荐

  1. 左神算法进阶班1_5BFPRT算法

    在无序数组中找到第k大的数1)分组,每N个数一组,(一般5个一组)2)每组分别进行排序,组间不排序3)将每个组的中位数拿出来,若偶数,则拿上 / 下中位数, 成立一个一个新数组.4)新数组递归调用BF ...

  2. Mysql优化系列之索引性能

    实际上,前面的数据类型和表结构设计优化不能算优化,只能算规范,也就是说在设计表的时候,应该且必须做到这些 索引是sql优化的核心部分,在<高性能Mysql>中单独抽出一章讲,也印证了其重要 ...

  3. Loadrunner 性能测试工具笔记

    性能的是的基础知识 什么是负载? 系统实际用户:可能会有很多人使用同一个系统,但并不是所有用户都回同时使用该系统,所以系统的实际用户是一个容量问题,而不是负载的问题 系统在线用户:当系统用户对系统进行 ...

  4. SpringMVC的Hello World

    本次使用Maven和Spring IO platform创建SpringMVC的Hello World. 一.Maven的Pom文件内容如下: <project xmlns="http ...

  5. Map和Reduce函数

  6. spring:AOP面向切面编程(注解)03

    使用注解写aop时最好使用环绕通知写 切面类: /** * 用于记录日志的工具类,它里面提供了公共的代码 */ @Component("logger") @Aspect //表示当 ...

  7. win7 删除多余启动项的方法

    win7已经没有像xp那么简单的boot.ini让我们修改了,取而代之的是bcdedit.现在就简单的说下bcdedit的常规应用吧.开始,运行,输入bcdedit /?可以看到帮助.简单的应用开始. ...

  8. leetcode146周赛-1131-绝对值表达式的最大值

    题目描述: class Solution: def maxAbsValExpr(self, arr1, arr2) -> int: def function(s1,s2): result1=[] ...

  9. springMVC or response redirect https

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> < ...

  10. VS2010-MFC(VS2010应用程序工程中文件的组成结构)

    转自:http://www.jizhuomi.com/software/143.html 用应用程序向导生成框架程序后,我们可以在之前设置的Location下看到以解决方案名命名的文件夹,此文件夹中包 ...