PHP 定时任务的另类实现

写个定时任务,没有权限。其实蛮好的,只要写业务逻辑然后交给 别人 挂服务器上就O了。

有些蛋疼的是,要是搬服务器呢?所以心血来潮,虫子上脑,耗点服务器资源了,不存在硬件上的问题。

有时候弄点东西,先不要考虑哪些杂七杂八的东西,可以节省很多时间。

原理如下:

1.curl调起1毫秒后 挂起运行 

2.调起 “”条件“”  死循环,满足 条件从条件中删除不进行下一次循环,不满足进入下一次循环

3.死循环 休息机制,防止一直跑,把服务器给弄没了

4.死循环跳出

5.再次调起运行条件

代码如下:

<?php

namespace app\shop\controller\sys_admin;

use app\AdminController;
use think\facade\Cache;
use think\Db;

/* ------------------------------------------------------ */

//订单相关
/* ------------------------------------------------------ */
/**
 * @author lianyu
 */

class Lianyu extends AdminController {

    private $redis_status = [
        "create", //创建时,超时处理
        "shipping", //发货时,自动收货处理
        "sign"      //签收时,无售后处理 返佣
    ];
    private $shop_order_auto_cancel = 1; //超时取消
    private $shop_auto_sign_limit = 1; //自动签收
    private $shop_after_sale_limit = 1; //关闭售后
    private $config;
    private $run_day = 30; //运行天数

    /* ------------------------------------------------------ */

    //-- 优先执行
    /* ------------------------------------------------------ */
    public function initialize() {
        parent::initialize();
        $this->Model = new \app\mainadmin\model\SettingsModel();
        $this->set_val();

//        $this->kill_var($setmod);
    }

    /* ------------------------------------------------------ */
    //-- 首页
    /* ------------------------------------------------------ */

    /**
     * 处理各定时状态
     */
    public function index() {
        $p4 = $this->calculate(); //手动添加
        //异步调起程序
        $request_url = $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; //当前URL
        $where = end(explode('/', $request_url));
        $path = str_replace($where, '', $request_url);
        $auto_cancel = $path . "auto_cancel";
//        $this->p($auto_cancel, 1, 0);
//        $this->doRequest($auto_cancel); //订单支付超时链接
        $p1 = $this->curl_request($auto_cancel);
        $auto_sign = $path . "auto_sign/type/4";
//        $this->p($auto_sign, 1, 0);
//        $this->doRequest($auto_sign); //订单签收超时链接
        $p2 = $this->curl_request($auto_sign);
        $after_sale = $path . "after_sale/type/4";
//        $this->p($after_sale, 1, 0);
//        $this->doRequest($after_sale); //订单超时链接
        $p3 = $this->curl_request($after_sale);

        echo "end";
    }

    /**
     * 计算是否断开过,搜索数据库,然后存入redis
     */
    function calculate() {
        $time = time();
        //redis操作
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379); //开放端口
        $redis->auth(); //密码
        $redis->select(0); //选择数据库
        $order_mod = new \app\shop\model\OrderModel();
        $size = (int) @readfile('./error/auto_cancel.txt'); //程序最后更新时间
        if ($size + 10 < $time) {
            //获取下单未支付未取消订单 ID
            $where = "confirm_time > 0  and pay_time = 0 and cancel_time = 0 ";
            $ary = $order_mod->field("order_id")->where($where)->select()->toArray();
            foreach ($ary as $val) {
                $redis->lPush($this->redis_status[0], $val['order_id']); //加入队列 返回队列总数
            }
        }
        $size = (int) @readfile('./error/auto_sign.txt');
        if ($size + 10 < $time) {
            //获取发货未签收订单 ID
            $where = "shipping_time > 0  and sign_time = 0 and cancel_time = 0 ";
            $ary = $order_mod->field("order_id")->where($where)->select()->toArray();
            foreach ($ary as $val) {
                $redis->lPush($this->redis_status[1], $val['order_id']); //加入队列 返回队列总数
            }
        }
        $size = (int) @readfile('./error/after_sale.txt');
        if ($size + 10 < $time) {
            //获取签收订单售后期内订单 ID
            $where = "sign_time > 0  and after_sale_qualification = 1";
            $ary = $order_mod->field("order_id")->where($where)->select()->toArray();
            foreach ($ary as $val) {
                $redis->lPush($this->redis_status[2], $val['order_id']); //加入队列 返回队列总数
            }
        }
    }

    /**
     * 设置参数
     */
    function set_val() {
        $this->config = config('config.');
        $this->shop_order_auto_cancel = $this->Model->where("name", "shop_order_auto_cancel")->value("data");
        $this->shop_auto_sign_limit = $this->Model->where("name", "shop_auto_sign_limit")->value("data");
        $this->shop_after_sale_limit = $this->Model->where("name", "shop_after_sale_limit")->value("data");
        $this->shop_order_auto_cancel*=60; //得出超时 秒
        $this->shop_auto_sign_limit*=86400; //得出自动签收 秒
        $this->shop_after_sale_limit*=86400; //得出无法售后时间 秒
        //测试使用
//        $this->shop_order_auto_cancel = 5;
//        $this->shop_auto_sign_limit = 5;
//        $this->shop_after_sale_limit = 5;
    }

    /**
     * 订单下单超时处理,未支付状态超时处理
     */
    function auto_cancel() {
        ignore_user_abort(true); // 忽略客户端断开 
        set_time_limit(0);    // 设置执行不超时
        $time = time();
        $action = $this->request->action();
        /**
         * 读取文件秒数
         * 如果与当前时间不超过10秒
         * 则说明程序正在运行,不再再次运行。
         * 可以多次调用该程序,从而加快从redis拿出的频率,提高处理速度,相对 多消耗一倍服务器资源
         * 这里只做一次,唯一调用
         */
        $size = (int) @readfile('./error/' . $action . '.txt');
        if ($size + 10 > $time) {
            return true;
        } else {
            //初始化参数
            data_log_w("1", $action . "_day.txt"); //日期参数
            $i = 1; //循环次数
            $maxi = 10000; //多少次循环休息
            $sleep = 1; //休息时间
            $_log = '超时自动取消'; //日志sql写入
        }
        $order_mod = new \app\shop\model\OrderModel();

        //redis操作
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379); //开放端口
        $redis->auth(); //密码
        $redis->select(0); //选择数据库
        while (TRUE) {
            $now_time = time();
            data_log_w($now_time, $action . ".txt"); //更新判断次程序运行文件
            $order_id = $redis->lPop($this->redis_status[0]); //出队 $this->Model->redis_sel($this->redis_status[0]);
//            output($order_id);
            if (empty($order_id)) {
                sleep($sleep); //休息
                continue;
            }
            //每10000次休息1秒中,每秒处理1w条
            if ($i > $maxi) {
                $i = 1;
                sleep($sleep);
            }
            $i++;

            // 如果订单不存在超时情况,释放队列进入下一次循环
            if ($this->shop_order_auto_cancel == 0) {
                continue;
            }
            $day = (int) @readfile('./error/' . $action . '_day.txt');
            //运行超过30天则停掉该程序,重新调用
            if ($day > $this->run_day) {
                data_log("redis 超时订单处理等待再次调用,运行时间" . date("Y-m-d H:i:s", $time) . " 到 " . date("Y-m-d H:i:s", $now_time), "run.txt");
                break;
            }
            /* 每隔1天刷新后台设置 */
            if (($now_time - $time) > ($day * 86400)) {
                ++$day;
                data_log_w($day, $action . "_day.txt");
                $this->set_val(); //
            }

            //获取订单信息
            $where['order_id'] = $order_id;
            $order = $order_mod->where($where)->find(); //确认时间
            //已支付,释放队列
            if ($order['pay_time'] > 0 || empty($order)) {
                continue;
            } elseif (($order['add_time'] + $this->shop_order_auto_cancel) < $now_time) {

                //订单超时处理
                $mkey = 'OrderIng_' . $order_id;
                Cache::set($mkey, 1);
                $upData['order_id'] = $order_id;
                $upData['order_status'] = $this->config['OS_CANCELED'];
                $upData['cancel_time'] = $now_time;
                $upData['update_time'] = $now_time; //最后更新时间
                $res = $order_mod->upInfo($upData);
//                output($res);
                Cache::rm($mkey);
                if ($res === true) {
                    $orderInfo = $order_mod->info($order_id);
                    $order_mod->_log($orderInfo, $_log);
                }
            } else {
                //订单没有超时,也没有其他操作,重新放回队列
                $redis->lPush($this->redis_status[0], $order_id); //加入队列 返回队列总数
            }

            //内存清理
            $this->kill_var($now_time);
            $this->kill_var($order_id);
            $this->kill_var($day);
            $this->kill_var($where);
            $this->kill_var($order);
            $this->kill_var($mkey);
            $this->kill_var($upData);
            $this->kill_var($res);
            $this->kill_var($orderInfo);
        }
        exit();
    }

    /**
     * 订单自动签收
     */
    function auto_sign() {
        ignore_user_abort(true); // 忽略客户端断开 
        set_time_limit(0);    // 设置执行不超时
        $time = time();
        $action = $this->request->action();
        /**
         * 读取文件秒数
         * 如果与当前时间不超过10秒
         * 则说明程序正在运行,不再再次运行。
         * 可以多次调用该程序,从而加快从redis拿出的频率,提高处理速度,相对 多消耗一倍服务器资源
         * 这里只做一次,唯一调用
         */
        $size = (int) @readfile('./error/' . $action . '.txt');
        if ($size + 10 > $time) {
            return true;
        } else {
            //初始化参数
            data_log_w("1", $action . "_day.txt"); //日期参数
            $i = 1; //循环次数
            $maxi = 10000; //多少次循环休息
            $sleep = 1; //休息时间
            $_log = '超时自动签收'; //日志sql写入
        }
        $order_mod = new \app\shop\model\OrderModel();
        $mod = new \app\member\controller\api\Zx();
        //redis操作
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379); //开放端口
        $redis->auth(); //密码
        $redis->select(0); //选择数据库
        while (TRUE) {
            $now_time = time();
            data_log_w($now_time, $action . ".txt"); //更新判断次程序运行文件
            $order_id = $redis->lPop($this->redis_status[1]); //出队 
//            output($order_id);
            if (empty($order_id)) {
                sleep($sleep); //休息
                continue;
            }
            //每10000次休息1秒中,每秒处理1w条
            if ($i > $maxi) {
                $i = 1;
                sleep($sleep);
            }
            $i++;

            // 如果订单不存在收货超时情况,释放队列进入下一次循环
            if ($this->shop_auto_sign_limit == 0) {
                continue;
            }
            $day = (int) @readfile('./error/' . $action . '_day.txt');
            //运行超过30天则停掉该程序,重新调用
            if ($day > $this->run_day) {
                data_log("redis 超时自动签收订单等待再次调用,运行时间" . date("Y-m-d H:i:s", $time) . " 到 " . date("Y-m-d H:i:s", $now_time), "run.txt");
                break;
            }
            /* 每隔1天刷新后台设置 */
            if (($now_time - $time) > ($day * 86400)) {
                ++$day;
                data_log_w($day, $action . "_day.txt");
                $this->set_val(); //
            }

            //获取订单信息
            $where['order_id'] = $order_id;
            $order = $order_mod->where($where)->find(); //订单信息
            //已收货,释放队列
            if ($order['sign_time'] > 0 || empty($order)) {
                continue;
            } elseif (($order['shipping_time'] + $this->shop_auto_sign_limit) < $now_time) {
                //订单签收超时处理
                $mkey = 'OrderIng_' . $order_id;
                Cache::set($mkey, 1);
                $upData['order_id'] = $order_id;
                $upData['shipping_status'] = $this->config['SS_SIGN'];
                $upData['sign_time'] = $now_time;
                $upData['update_time'] = $now_time; //最后更新时间
                $res = $order_mod->upInfo($upData);
                Cache::rm($mkey);
                if ($res === true) {
                    $orderInfo = $order_mod->info($order_id);
                    $order_mod->_log($orderInfo, $_log);
                    $mod->pid_bonuses($order_id); //返佣处理
//                    $redis->lPush($this->redis_status[2], $order_id);//加入售后队列
                }
            } else {
                //订单没有超时,也没有其他操作,重新放回队列
                $redis->lPush($this->redis_status[1], $order_id); //加入队列 返回队列总数
            }

            //内存清理
            $this->kill_var($now_time);
            $this->kill_var($order_id);
            $this->kill_var($day);
            $this->kill_var($where);
            $this->kill_var($order);
            $this->kill_var($mkey);
            $this->kill_var($upData);
            $this->kill_var($res);
            $this->kill_var($orderInfo);
        }
        exit();
    }

    /**
     * 订单自动无售后
     */
    function after_sale() {
        ignore_user_abort(true); // 忽略客户端断开 
        set_time_limit(0);    // 设置执行不超时
        $time = time();
        $action = $this->request->action();
        /**
         * 读取文件秒数
         * 如果与当前时间不超过10秒
         * 则说明程序正在运行,不再再次运行。
         * 可以多次调用该程序,从而加快从redis拿出的频率,提高处理速度,相对 多消耗一倍服务器资源
         * 这里只做一次,唯一调用
         */
        $size = (int) @readfile('./error/' . $action . '.txt');
        if ($size + 10 > $time) {
            return true;
        } else {
            //初始化参数
            data_log_w("1", $action . "_day.txt"); //日期参数
            $i = 1; //循环次数
            $maxi = 10000; //多少次循环休息
            $sleep = 1; //休息时间
            $_log = '售后过期'; //日志sql写入
        }
        $order_mod = new \app\shop\model\OrderModel();
        $mod = new \app\member\controller\api\Zx();

        //redis操作
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379); //开放端口
        $redis->auth(); //密码
        $redis->select(0); //选择数据库
        while (TRUE) {
            $now_time = time();
            data_log_w($now_time, $action . ".txt"); //更新判断次程序运行文件
            $order_id = $redis->lPop($this->redis_status[2]); //出队 
//            output($order_id);
            if (empty($order_id)) {
                sleep($sleep); //休息
                continue;
            }
            //每10000次休息1秒中,每秒处理1w条
            if ($i > $maxi) {
                $i = 1;
                sleep($sleep);
            }
            $i++;

            // 如果订单不存在收货超时情况,释放队列进入下一次循环
            if ($this->shop_after_sale_limit == 0) {
                continue;
            }
            $day = (int) @readfile('./error/' . $action . '_day.txt');
            //运行超过30天则停掉该程序,重新调用
            if ($day > $this->run_day) {
                data_log("redis 售后超时订单等待再次调用,运行时间" . date("Y-m-d H:i:s", $time) . " 到 " . date("Y-m-d H:i:s", $now_time), "run.txt");
                break;
            }
            /* 每隔1天刷新后台设置 */
            if (($now_time - $time) > ($day * 86400)) {
                ++$day;
                data_log_w($day, $action . "_day.txt");
                $this->set_val(); //
            }

            //获取订单信息
            $where['order_id'] = $order_id;
            $order = $order_mod->where($where)->find(); //订单信息
            //已售后,释放队列
            if ($order['after_sale_qualification'] == 0 || empty($order) || $order['is_after_sale'] != 0) {
                continue;
            } elseif (($order['sign_time'] + $this->shop_after_sale_limit) < $now_time) {
                //订单签收超时处理
                $upData['order_id'] = $order_id;
                $upData['after_sale_qualification'] = 0;
                $upData['update_time'] = $now_time; //最后更新时间
                $res = $order_mod->upInfo($upData);
                if ($res === true) {
                    $orderInfo = $order_mod->info($order_id);
                    $order_mod->_log($orderInfo, $_log);
                    $mod->pid_bonuses($order_id, 1); //返佣处理
                }
            } else {
                //订单没有超时,也没有其他操作,重新放回队列
                $redis->lPush($this->redis_status[2], $order_id); //加入队列 返回队列总数
            }

            //内存清理
            $this->kill_var($now_time);
            $this->kill_var($order_id);
            $this->kill_var($day);
            $this->kill_var($where);
            $this->kill_var($order);
            $this->kill_var($upData);
            $this->kill_var($res);
            $this->kill_var($orderInfo);
        }
        exit();
    }

    /**
     * 异步访问
     * @param type $url     需要访问的url
     * @param type $param   携带参数
     */
    function doRequest($url, $param = array()) {
        $urlinfo = parse_url($url);
        $host = $urlinfo['host'];
        $path = $urlinfo['path'];
        $query = isset($param) ? http_build_query($param) : '';
        $port = 80;
        $errno = 0;
        $errstr = '';
        $timeout = 10;
        $fp = fsockopen($host, $port, $errno, $errstr, $timeout);
        $out = "POST " . $path . " HTTP/1.1\r\n";
        $out .= "host:" . $host . "\r\n";
        $out .= "content-length:" . strlen($query) . "\r\n";
        $out .= "content-type:application/x-www-form-urlencoded\r\n";
        $out .= "connection:close\r\n\r\n";
        $out .= $query;
        fputs($fp, $out);
        fclose($fp);
    }

    //参数1:访问的URL,参数2:响应超时时间 毫秒
    function curl_request($url = "http://www.baidu.com", $ms = 100) {
        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_NOSIGNAL, 1); //注意,毫秒超时一定要设置这个
        curl_setopt($curl, CURLOPT_TIMEOUT_MS, $ms); //超时毫秒,cURL 7.16.2中被加入。从PHP 5.2.3起可使用
//        curl_setopt($curl, CURLOPT_TIMEOUT, $s);//超时时间 秒
//        curl_setopt($curl, CURLOPT_HEADER, 1);//设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //设置获取的信息以文件流的形式返回,而不是直接输出。
        //执行命令
        $data = curl_exec($curl);
        //关闭URL请求
        curl_close($curl);
        //显示获得的数据
        return $data;
    }

    /**
     * 每日零点,自动将账户内当前的快乐豆按当前后台设定的比例自动转化为余额;
     * 激活条件: 时间处于每日 00:00:00 到 00:00:59之间
     * 运算逻辑: 
     * users_account 表 快乐豆字段清零
     *  设置临时表数据(数据复制一份到新表)无数据锁定
     *  临时表数据运算 更新存储表(已有快乐豆-新表快乐豆 = 现有快乐豆 无今天更新情况下 为0 )
     *  添加日志
     *  临时表数据清0 truncate
     *  数据可能超百万,采用多线程分批处理,每批处理1w,采用异步处理
     *  
     * 获取比例,得出余额
     * 用户余额添加 比例余额
     * 添加快乐豆日志表
     * 添加余额日志表
     */
    function ff() {
        ignore_user_abort(true); // 忽略客户端断开 
        set_time_limit(0);    // 设置执行不超时
        $now_time = time();
        if (date("H:i", $now_time) != "00:00") {
            $this->error("运行时间错误");
        }
        $bl = $this->Model->where("name", "intergral")->value("data"); //获取比例
        if (empty($bl)) {
            $this->error("比例未设置");
        }
        $p = input("p", 1);
        $row = 10000; //每次处理一万条数据
        $limit = ($p - 1) * $row . "," . $row;
        if ($p == 1) {
            Db::query("drop table users_account_copy;"); //删除表
            $res = Db::query("create table users_account_copy (select user_id,((use_integral*100 div " . $bl . ")/100) as balance_money2,"
                            . "use_integral,balance_money from users_account where use_integral >0);"); //表生成
            if ($res === false) {
                $this->error("Create Table Error.");
            }
        }
        $count = Db::table("users_account_copy")->count();
        if ($count > (($p - 1) * $row)) {
            ++$p;
            //异步处理
            $request_url = $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; //当前URL
            $where = end(explode('/', $request_url));
            $path = str_replace($where, '', $request_url);
            $auto_cancel = $path . "ff/p/" . $p;
            $this->curl_request($auto_cancel); //异步内循环
        }
        $res = Db::table("users_account_copy")->order("user_id asc")->limit($limit)->select();
        if (empty($res)) {
            $this->error("Not data end.");
        }

//        $model = new \app\member\model\AccountModel();
        $accout_log = new \app\member\model\AccountLogModel();
//        $usermod=new \app\member\model\UsersModel();
        //数据处理
        foreach ($res as $val) {
            //用户更新数据
            $sql = "update users_account set balance_money=balance_money+" . $val['balance_money2'] . ",use_integral=use_integral-" . $val['use_integral']
                    . " where user_id = " . $val['user_id'];
            Db::query($sql);
            $data=Db::table("users_account")->where("user_id",$val['user_id'])->find();
            
            //用户日志记录写入
            $insert = [
                "old_total_dividend" => $data['total_dividend'],
                "total_dividend" => $data['total_dividend'],
                "total_integral" => $val['use_integral'],
                "old_total_integral" => $val['use_integral']+$data['use_integral'],
                "balance_money" => $val['balance_money2'],
                "old_balance_money" => $data['balance_money']-$val['balance_money2'],
                "use_integral" => $val["use_integral"],
                "old_use_integral" => $val['use_integral']+$data['use_integral'],
                "change_time" => time(),
                "change_ip" => request()->ip(),
                "change_desc" => "快乐豆转余额",
                "change_type" => 3,
                "detail_type" => 0,
                "user_id" => $val['user_id'],
//                    "by_id"=>2,
            ];
            $res2 = $accout_log->add_log($insert);
            if($res2<=0){
                data_log($insert, "ff_err.txt");
            }
        }
    }

    function test() {
        echo "<xmm>1";
        echo $this->curl_request();
    }

}

Pss:有些懒,给自己一个放纵的理由,懒人有懒福。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值