吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3438|回复: 1
收起左侧

[其他原创] 【PHP】基于thinkphp3.2的一个防阻塞定时类

  [复制链接]
yqsphp 发表于 2019-3-20 13:21
本帖最后由 yqsphp 于 2019-3-20 13:30 编辑

很早之前自己写了一个php定时任务的类,不必用到windows或linux的定时计划任务,基于thinkphp3.2
我就不采用原生的PHP了,你们可以自己动手改成原生的,前端页面任务列表如何实现大家自己去做,我只提供后端的
原理:根据设置的最大执行时间默认30s,在程序执行到超过零界点时在重新请求_sock()这个方法,如此循环,永久执行(这里用到一个配置文件控制定时开关),已经跟浏览器无关了
问题:在非安全模式下即php.ini配置中:safe_mode= Off时设置 set_time_limit(0),测试n次,在window非安全模式下这个函数设置为0没什么用,在linux下可以。
因为反复调用自己,所有这个类中要不要set_time_limit(0)都无所谓了,但是睡眠时间不能超过最大执行时间

前端页面直接传值改变配置文件的值来控制定时开关,比如传 off = 1,以下代码中的定时任务的url做的是死的,前端定时任务列表可以直接传URL必须带host配置文件就是一个  return 1或0源码如下:
[PHP] 纯文本查看 复制代码
<?php
namespace Admin\Controller;
use Think\Controller;
/**
 * 事件定时任务
 * start(),stop()这两个方法用来测试
 */
class TimerController extends Controller{
    private static $url; //定时任务的url
    public function _initialize(){
        self::$url = 'http://'.$_SERVER['HTTP_HOST'].U('timer/app');
    }
    /**
     * 定时器任务,通过前端传值控制,
         * 启动后就跟浏览器无关了,除非服务重启
     * 增加定时配置文件timer.php
     */
    public function index(){
        if(IS_POST){
            $off = I('post.off');
            //启动定时
            if($off){
                file_put_contents(CONF_PATH.'timer.php', '<?php '.PHP_EOL.'return 1;');
                $this->_sock(self::$url);
            }else{
                file_put_contents(CONF_PATH.'timer.php', '<?php '.PHP_EOL.'return 0;');
            }
        }else{ 
                        $flag = include CONF_PATH.'timer.php';
                        $this->assign('status',$flag);
                        $this->display();
                }
    }
    /**
     * 启动定时器
     */
    public function start(){
        $this->_sock(self::$url);
    }
    /**
     * 停止定时器
     * 控制定时器这里采用修改文件真假值(timer.php)
     */
    public function stop(){
        $flag = require_once CONF_PATH.'timer.php';
        file_put_contents(CONF_PATH.'timer.php', '<?php '.PHP_EOL.'return 0;');
    }
    /**
     * 执行定时任务操作
     */
    private function app(){
        ignore_user_abort(true);  //忽略浏览器关闭
        set_time_limit(0);  //设置超时 在window非安全模式下这个函数设置为0没什么用,在linux下可以。-_-
        $flag = include CONF_PATH.'timer.php';
        if(!$flag) die('stop');
        /**
         * 这里是逻辑代码区,由自己实现
         */
        sleep(60); //睡眠时间,但不要超过最大执行时间,可以设置set_time_limit的值,建议不要设置0,200,300都行
        $this->_sock(self::$url);//循环执行
    }
     
    /**
         * 解析url,执行定时任务是防止阻塞
         * 
         * [url=home.php?mod=space&uid=952169]@Param[/url] $url 执行任务的url
         * [url=home.php?mod=space&uid=155549]@Return[/url] array|boolean
         */
        protected function _sock($url){
            //获取url主机
                $host         = parse_url($url, PHP_URL_HOST);
                //获取url端口并判断是否80
                $port         = parse_url($url, PHP_URL_PORT) ?: 80;
                //获取URL协议http或https
                $scheme = parse_url($url, PHP_URL_SCHEME);
                //获取host之后的url
                $path         = parse_url($url, PHP_URL_PATH);
                //获取传递参数获取的是?后面的
                $query         = parse_url($url, PHP_URL_QUERY);
                //将获取的参数拼接到url后面
                if($query) $path .= '?' . $query;
                //如果是https请求就改ssl协议请求
                if($scheme == 'https'){
                        $host = 'ssl://' . $host;
                }
                //$a = ['host'=>$host,'port'=>$port,'$scheme'=>$scheme,'$path'=>$path,'$query'=>$query];
                //dump($a);
                //打开一个网络socket连接
                $fp = fsockopen($host, $port, $error_code, $error_msg, 1);
                if(! $fp){
                        return [
                                'error_code' => $error_code,
                                'error_msg'  => $error_msg
                        ];
                }else{
                    // 开启了手册上说的非阻塞模式
                        stream_set_blocking($fp, 0); 
                        // 设置超时
                        stream_set_timeout($fp, 1); 
                        $header  = "GET $path HTTP/1.1\r\n";
                        $header .= "Host: $host\r\n";
                        $header .= "Connection: close\r\n\r\n"; // 长连接关闭
                        fwrite($fp, $header);
                        usleep(1000); // 如果没有这延时,可能在nginx服务器上就无法执行成功
                        fclose($fp);
                        return true;
                }
        }
     
}

下面的方法是自己现在用的仅供学习参考
[PHP] 纯文本查看 复制代码
/**
	 * 任务提醒
	 */
	private function app(){
		//每日早上8点清空S
		if(date('H') == 'O8') S('send_data',null); 
		$old_data = S('send_data');
		//file_put_contents('data', $old_data.PHP_EOL,FILE_APPEND);
		$child	= M('userContact')->where('ccnexttime <> "" and isgood = 1 and ccremind <> 0 and ccissend = 0')->order('ccctime desc')->buildSql();
		$cont   = M('userContact')->field('a.*,u.mail')->table($child.' a')->join('left join '.C('DB_PREFIX').'admin u on u.truename = a.ccuser')->group('a.userid')->order('a.ccctime desc')->select();
		//事先存储查询的数据,当再次循环查询是比较存储数据与查询数据是否一样
		$new_data = md5(serialize($cont));
		S('send_data',$new_data,1800);
		if($old_data != $new_data){
			foreach($cont as $val){
				if((strtotime($val['ccnexttime']) - time()) <= $val['ccbefore']*60){
					switch ($val['ccremind']){
						case 1:
							$data['wechat'][] = $val['ccuser']."你有待回访客户,请登录系统查看\n";
							break;
						case 2:
							$data['email'][$val['mail']][] = $val['ccuser'].'你有待回访客户,请登录系统查看<br/>';
							break;
						case 3:
							$data['mess'][] = $val['ccuser']."你有待回访客户,请登录系统查看\n";
							break;
					}
				}
			}
		
			//邮箱提醒
			if(!empty($data['email'])){
				foreach($data['email'] as $key=>$v){
					send_mail( $key, 'abcdef@qq.com', '任务提醒', implode(',', $v));
				}
			}
			//微信提醒
			if(!empty($data['wechat'])){
				$chat = Wechat::app(1);
				$data = [
					'touser'=>'关注微信号的原始id' ,//ocanwvquBsmKIabcvElB1KJpcNAEg-QM
					'msgtype'=>'text',
					'text'=>['content'=>implode(',', $data['wechat'])]
				];
				//$chat->sendMassMessage($data);
				$chat->sendCustomMessage($data);
				vendor('Wechat.error');
				file_put_contents('wx.txt', \Error::getError($chat->errCode).PHP_EOL,FILE_APPEND);
			}
			//短信提醒
			if(!empty($data['mess'])){
				$sms = new Sms(C('sms_require'), C('sms_port'), C('sms_version'));
				$datas = $sms->sendTemplateSMS('你的电话号码', $data['mess'], 1);
			}
		}
	}

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

limit81995 发表于 2019-8-2 15:13
终于找到了这种资源,收藏了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 12:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表