訂單是咱們在平常開發中常常會遇到的一個功能,最近在作一個訂單過時與超時的開發。訂單過時與超時就不用我解釋了吧,其實二者都是同一個問題來着,就是訂單未支付的處理,咱們要作的是對這些未支付的訂單到了必定時間就自動取消,好了,你第一反應那確定就是作一個定時任務了!是的,就是定時任務,可是哪一個纔會是最佳方案呢,下面來看看:php
1、前端到時間請求取消html
你確定不會想着在前端來作定時請求取消該訂單的,由於若是客戶禁用了該應用或者沒聯網,那到了必定時間時,還一直都是未處理狀態的。因此前端通常都是手動去觸發取消訂單的。前端
2、服務端定時查詢有沒有須要取消的訂單,而後批量處理。node
這種方法咱們用得最多的,不過存在準確度的問題,還有須要確認定時任務的週期,可是咱們能夠結合redis來實現,咱們能夠在存redis時候順便存在mysql這樣的數據庫作長久存儲而後用服務端定時查詢,這裏我用到了redis的訂閱功能。mysql
首先先修改一下配置redis
而後再重啓redissql
這裏建立4個文件shell
index.php 建立訂單,發佈消息,10s後查詢訂單狀態並更新訂單數據庫
<?php
require_once 'Redis2.class.php';
require_once 'db.class.php';
$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-d H:i:s',time())];
$mysql->insert('order',$data);
$order_sn = $data['ordersn'];
$redis2->setex($order_sn,10,$order_sn);
psubscribe.php,發佈redis訂閱的一些邏輯處理數組
這裏我把redis的一些操做和mysql的一些處理作了封裝處理,這個大家能夠用到大家本身的項目當中的
Redis2.class.php
<?php
class Redis2
{
private $redis;
public function __construct($host = '127.0.0.1', $port = 6379)
{
$this->redis = new Redis();
$this->redis->connect($host, $port);
$this->redis->auth('123456');
}
public function setex($key, $time, $val)
{
return $this->redis->setex($key, $time, $val);
}
public function set($key, $val)
{
return $this->redis->set($key, $val);
}
public function get($key)
{
return $this->redis->get($key);
}
public function expire($key = null, $time = 0)
{
return $this->redis->expire($key, $time);
}
public function psubscribe($patterns = array(), $callback)
{
$this->redis->psubscribe($patterns, $callback);
}
public function setOption()
{
$this->redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
}
public function lRange($key,$start,$end)
{
return $this->redis->lRange($key,$start,$end);
}
public function lPush($key, $value1, $value2 = null, $valueN = null ){
return $this->redis->lPush($key, $value1, $value2 = null, $valueN = null );
}
public function delete($key1, $key2 = null, $key3 = null)
{
return $this->redis->delete($key1, $key2 = null, $key3 = null);
}
}
db.class.php,對sql處理的一些封裝
<?php
class mysql
{
private $mysqli;
private $result;
/**
* 數據庫鏈接
* @param $config 配置數組
*/
public function connect()
{
$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 = new mysqli($host, $username, $password, $database, $port);
}
/**
* 數據查詢
* @param $table 數據表
* @param null $field 字段
* @param null $where 條件
* @return mixed 查詢結果數目
*/
public function select($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;
}
/**
* @return mixed 獲取所有結果
*/
public function fetchAll()
{
return $this->result->fetch_all(MYSQLI_ASSOC);
}
/**
* 插入數據
* @param $table 數據表
* @param $data 數據數組
* @return mixed 插入ID
*/
public function insert($table, $data)
{
foreach ($data as $key => $value) {
$data[$key] = $this->mysqli->real_escape_string($value);
}
$keys = '`' . implode('`,`', array_keys($data)) . '`';
$values = '\'' . implode("','", array_values($data)) . '\'';
$sql = "INSERT INTO `{$table}`( {$keys} )VALUES( {$values} )";
$this->mysqli->query($sql);
return $this->mysqli->insert_id;
}
/**
* 更新數據
* @param $table 數據表
* @param $data 數據數組
* @param $where 過濾條件
* @return mixed 受影響記錄
*/
public function update($table, $data, $where)
{
foreach ($data as $key => $value) {
$data[$key] = $this->mysqli->real_escape_string($value);
}
$sets = array();
foreach ($data as $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 過濾條件
* @return mixed 受影響記錄
*/
public function delete($table, $where)
{
$sql = "DELETE FROM `{$table}` WHERE {$where}";
$this->mysqli->query($sql);
return $this->mysqli->affected_rows;
}
}
對每一次訂單的訪問咱們作了服務器監放任務,以下:
設置本地域名並訪問http://www.startphp.cn/index.php
此時,每訪問一次index.php,就會建立一個訂單,10s鍾後,若是該訂單依舊處於未支付狀態,就設置訂單失效。
可是php的cli模式在服務器運行後,老是會掉線,處理這個問題的方法,咱們不妨寫一個腳本
1.編寫shell腳本,定時檢查進程是否存在,不存在的話就開啓服務,而且將運行狀況寫入日誌
腳本文件以下:
#!/bin/sh
PIDS=`pidof php`
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)" start php start....." >> /mytask/task.start.log
cd /var/www/html/redis
php psubscribe.php &
echo -e $(date +%Y"."%m"."%d" "%k":"%M":"%S)" start php success....." >> /mytask/task.start.log
fi
在crontab任務裏建立任務,這裏設定的是每5s檢查一次,crontab -e
效果你能夠查看task.run.log
結果以下:
有須要學習交流的友人請加入交流羣的我們一塊兒,羣內都是1-7年的開發者,但願能夠一塊兒交流,探討PHP,swoole這塊的技術 或者有其餘問題 也能夠問,獲取swoole或者php進階相關資料私聊管理便可
別忘了點贊哦,按期分享乾貨