文章来自:php自学中心网站
链接:/front/php/122378.html
作者:磊丰
商务合作:请加微信(QQ):2230304070
视频教程
码农网每天更新3个教程:158RMB升级永久会员,即可查看网站全部视频教程。有问题请咨询微信:2230304070
文章正文
订单是我们在日常开发中经常会遇到的一个功能,最近在做一个订单过期与超时的开发。订单过期与超时就不用我解释了吧,其实两者都是同一个问题来着,就是订单未支付的处理,我们要做的是对这些未支付的订单到了一定时间就自动取消,好了,你第一反应那肯定就是做一个定时任务了!是的,就是定时任务,但是哪个才会是最佳方案呢,下面来看看:
一、前端到时间请求取消
你肯定不会想着在前端来做定时请求取消该订单的,因为如果客户禁用了该应用或者没联网,那到了一定时间时,还一直都是未处理状态的。所以前端一般都是手动去触发取消订单的。
二、服务端定时查询有没有需要取消的订单,然后批量处理。
这种方法我们用得最多的,不过存在准确度的问题,还有需要确认定时任务的周期,但是我们可以结合redis来实现,我们可以在存redis时候顺便存在mysql这样的数据库做长久存储然后用服务端定时查询,这里我用到了redis的订阅功能。
首先先修改一下配置
notify-keyspace-events“Ex”#x 代表了过期事件。vim/etc/redis/redis.conf
然后再重启redis
serviceredisrestart
这里创建4个文件
index.php创建订单,发布消息,10s后查询订单状态并更新订单
$redis2=new\Redis2(); $list=$redis2->lRange('order',0,9999999); $mysql=new\mysql(); $mysql->connect(); $data=['ordersn'=>'SN'.time().'T'.rand(10000000,99999999),'status'=>0,'createtime'=>date('Y-m-dH:i:s',time())]; $mysql->insert('order',$data); $order_sn=$data['ordersn']; $redis2->setex($order_sn,10,$order_sn);<?php require_once'Redis2.class.php';require_once'db.class.php';
psubscribe.php,发布redis订阅的一些逻辑处理
ini_set('default_socket_timeout',-1);//设置不超时require_once'./Redis.class.php';require_once'./db.class.php'; $redis=new\Redis2();//解决Redis客户端订阅时候超时情况 $redis->setOption(); $redis->psubscribe(array('__keyevent@0__:expired'),'keyCallback');//回调函数,这里写处理逻辑functionkeyCallback($redis,$pattern,$chan,$msg){ $mysql=new\mysql(); $mysql->connect(); $where="ordersn='".$msg."'"; $mysql->select('order','',$where); $finds=$mysql->fetchAll();if(isset($finds[0]['status'])&&$finds[0]['status']==0){ $data=array('status'=>3,'updatetime'=>date('Y-m-dH:i:s',time())); $where="id=".$finds[0]['id']; $mysql->update('order',$data,$where); } }<?php
这里我把redis的一些操作和mysql的一些处理做了封装处理,这个你们可以用到你们自己的项目当中的
Redis2.class.php
}publicfunctionsetex($key,$time,$val){return$this->redis->setex($key,$time,$val); }publicfunctionset($key,$val){return$this->redis->set($key,$val); }publicfunctionget($key){return$this->redis->get($key); }publicfunctionexpire($key=null,$time=0){return$this->redis->expire($key,$time); }publicfunctionpsubscribe($patterns=array(),$callback){$this->redis->psubscribe($patterns,$callback); }publicfunctionsetOption(){$this->redis->setOption(\Redis::OPT_READ_TIMEOUT,-1); }publicfunctionlRange($key,$start,$end){return$this->redis->lRange($key,$start,$end); }publicfunctionlPush($key,$value1,$value2=null,$valueN=null){return$this->redis->lPush($key,$value1,$value2=null,$valueN=null); }publicfunctiondelete($key1,$key2=null,$key3=null){return$this->redis->delete($key1,$key2=null,$key3=null); } }<?php classRedis2{private$redis;publicfunction__construct($host='127.0.0.1',$port=6379){$this->redis=newRedis();$this->redis->connect($host,$port);$this->redis->auth('123456');
db.class.php,对sql处理的一些封装
*数据库连接 *@param$config配置数组 */publicfunctionconnect(){ $config=array('host'=>'127.0.0.1','username'=>'root','password'=>'123456qwerty','database'=>'marhal','port'=>3306, ); $host=$config['host'];//主机地址 $username=$config['username'];//用户名 $password=$config['password'];//密码 $database=$config['database'];//数据库 $port=$config['port'];//端口号$this->mysqli=newmysqli($host,$username,$password,$database,$port); }/** *数据查询 *@param$table数据表 *@paramnull$field字段 *@paramnull$where条件 *@returnmixed查询结果数目 */publicfunctionselect($table,$field=null,$where=null){ $sql="SELECT*FROM`{$table}`";//echo$sql;exit;if(!empty($field)){ $field='`'.implode('`,`',$field).'`'; $sql=str_replace('*',$field,$sql); }if(!empty($where)){ $sql=$sql.'WHERE'.$where; }$this->result=$this->mysqli->query($sql);return$this->result; }/** *@returnmixed获取全部结果 */publicfunctionfetchAll(){return$this->result->fetch_all(MYSQLI_ASSOC); }/** *插入数据 *@param$table数据表 *@param$data数据数组 *@returnmixed插入ID */publicfunctioninsert($table,$data){foreach($dataas$key=>$value){ $data[$key]=$this->mysqli->real_escape_string($value); } $keys='`'.implode('`,`',array_keys($data)).'`'; $values='\''.implode("','",array_values($data)).'\''; $sql="INSERTINTO`{$table}`({$keys})VALUES({$values})";$this->mysqli->query($sql);return$this->mysqli->insert_id; }/** *更新数据 *@param$table数据表 *@param$data数据数组 *@param$where过滤条件 *@returnmixed受影响记录 */publicfunctionupdate($table,$data,$where){foreach($dataas$key=>$value){ $data[$key]=$this->mysqli->real_escape_string($value); } $sets=array();foreach($dataas$key=>$value){ $kstr='`'.$key.'`'; $vstr='\''.$value.'\''; array_push($sets,$kstr.'='.$vstr); } $kav=implode(',',$sets); $sql="UPDATE`{$table}`SET{$kav}WHERE{$where}";$this->mysqli->query($sql);return$this->mysqli->affected_rows; }/** *删除数据 *@param$table数据表 *@param$where过滤条件 *@returnmixed受影响记录 */publicfunctiondelete($table,$where){ $sql="DELETEFROM`{$table}`WHERE{$where}";$this->mysqli->query($sql);return$this->mysqli->affected_rows; } }<?php classmysql{private$mysqli;private$result;/**
对每一次订单的访问我们做了服务器监听任务,如下:
#即时监听,ctrl+c退出监听ctrl+z暂停监听 nohupphppsubscribe.php #后台监听 nohupphppsubscribe.php&cd/var/www/html/redis
设置本地域名并访问/index.php
此时,每访问一次index.php,就会创建一个订单,10s钟后,如果该订单依旧处于未支付状态,就设置订单失效。
这里要注意一下:
在命令之前加上 nohup ,启动的进程将会忽略linux的挂起信号 (SIGHUP)
这时候,当我们组合 nohup 和 &两种方式时,启动的进程不会占用控制台,并且不依赖控制台,控制台关闭之后,进程被1号进程收养,成为孤儿进程,这就和守护进程的机制非常类似了,并且nohup默认会把程序的输出重定向到当前目录下的nohup.out文件,如果没有可写权限,则写入 $homepath/nohup.out
但是php的cli模式在服务器运行后,总是会掉线,处理这个问题的方法,我们不妨写一个脚本
1.编写shell脚本,定时检查进程是否存在,不存在的话就开启服务,并且将运行情况写入日志
mkdirmytask cdmytask touchphprunning.sh viphprunning.shcd/
脚本文件如下:
PIDS=`pidofphp` if["$PIDS"!=""];then echo"在运行" echo-e$(date+%Y"."%m"."%d""%k":"%M":"%S)"running.....">>/mytask/task.run.log else echo"不在运行,开始启动" echo-e$(date+%Y"."%m"."%d""%k":"%M":"%S)"startphpstart.....">>/mytask/task.start.log cd/var/www/html/redis phppsubscribe.php& echo-e$(date+%Y"."%m"."%d""%k":"%M":"%S)"startphpsuccess.....">>/mytask/task.start.log fi#!/bin/sh
在crontab任务里创建任务,这里设定的是每5s检查一次,crontab -e
*****sleep10;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep15;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep20;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep25;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep30;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep35;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep40;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep45;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep50;sh/mytask/phprunning.sh>>/mytask/crontab.log *****sleep55;sh/mytask/phprunning.sh>>/mytask/crontab.log*****sleep5;sh/mytask/phprunning.sh>>/mytask/crontab.log
效果你可以查看task.run.log
cat/mytask/task.run.log
结果如下:
-e.10.2214:28:46running..... -e.10.2214:28:51running..... -e.10.2214:28:56running..... -e.10.2214:29:06running..... -e.10.2214:29:11running..... -e.10.2214:29:16running..... -e.10.2214:29:21running..... -e.10.2214:29:26running..... -e.10.2214:29:31running..... -e.10.2214:29:36running..... -e.10.2214:29:41running..... -e.10.2214:29:46running.....-e.10.2214:28:41running.....
以上是本文的全部内容,希望对大家的学习有帮助,也希望大家多多支持php自学中心感谢阅读!