With PHP + Redis achieve delayed tasks automatically cancel the order (detailed tutorial)

Simple regular tasks Solution: Use the redis keyspace notifications (notification event after bond failure) Note that this feature was introduced after redis version 2.8, so you reids on the server if the minimum version 2.8 above;

 

(A) business scenario:

1. When a business trigger need to start a timed task, go to perform a task within a specified time (eg automatically cancel the order, auto-complete orders and other functions)

2, redis of keyspace notifications will be sent a key event in the failure, listen to this event a client can receive notification

(B) Service to prepare:

1, modify the configuration file reids (redis.conf) [System configuration window file: redis.windows.conf]

redis default will not open keyspace notifications, because there would be cpu consumption after opening

Note: E: keyevent events, events __keyevent @ <db> __ publish as a prefix;

x: expired event, when a key expired and will have to delete the event;

The original configuration as follows:

notify-keyspace-events ""

Change the configuration as follows:

notify-keyspace-events "Ex"

 

After saving the configuration, restart Redis service, validate the configuration

[root@chokingwin etc]#
service redis-server restart /usr/local/redis/etc/redis.conf 
Stopping redis-server: [ OK ] 
Starting redis-server: [ OK ]

redis window system restart, before switching to redis file directory, and then close redis service (redis-server --service-stop), then turn (redis-server --service-start)

C) file code:

phpredis achieve subscribe Keyspace notification, can automatically cancel the order, the order automatically. The following is the test case

Create four files, and then modify the database configuration parameters yourself redis

db.class.php

<? phpclass the mysqli MySQL $ {Private; Private $ Result; / ** 

     * Database Connectivity 

     * @param $ config configuration array * / 

 

    public function Connect () 

    {$ config = Array ( 'Host' => '127.0.0.1', 

            'username' => 'the root', 

            'password' => '168 168', 

            'Database' => 'Test', 

            'Port' => 3306, 

        ); host = $ $ config [ 'host']; // host address 

        $ username = $ config [ 'username ']; // username 

        $ password = $ config [ 'password ']; // password 

        $ database = $ config [ 'database ']; // database 

        $ port = $ config [ 'port'];//端口号

        $this->mysqli = new mysqli($host, $username, $password, $database, $port);

 

    } / ** 

     * data query 

     * @param $ table data table 

     * @param null $ field field 

     * @param null $ where conditions 

     * @return mixed number query result * / 

    public function SELECT (Table $, $ Field = null, $ = null WHERE) 

    {$ SQL = "{$` the SELECT * the FROM Table} `"; // echo $ SQL; Exit; 

        . IF (! empty ($ Field)) = {Field $ ' `' The implode ( '`, ` ', $ Field).'` '; $ SQL = str_replace (' * ', $ Field, $ SQL); 

        .!} IF (empty ($ WHERE)) {$ SQL = $ SQL' the WHERE '$ WHERE. ; 

        } $ this-> Result = $ this-> mysqli-> Query ($ SQL); return $ this-> Result;

    } / ** 

     * @return Mixed obtain all the results * / 

    public function the 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 $ data array data
     * @param $ table data table
     * update 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 $ where the filter conditions
     * @param $ table data table
     * delete data

    } / **




     * @return mixed 受影响记录     */

    public function delete($table, $where)

    {        $sql = "DELETE FROM `{$table}` WHERE {$where}";        $this->mysqli->query($sql);        return $this->mysqli->affected_rows;

    }

}

  

index.php

<?php

 

require_once 'Redis2.class.php';

 

$redis = new \Redis2('127.0.0.1','6379','','15');

$order_sn   = 'SN'.time().'T'.rand(10000000,99999999);

 

$use_mysql = 1;         //是否使用数据库,1使用,2不使用

if($use_mysql == 1){

   /*

    *   //数据表

    *   CREATE TABLE `order` (

    *      `ordersn` varchar(255) NOT NULL DEFAULT '',

    *      `status` varchar(255) NOT NULL DEFAULT '',

    *      `createtime` varchar(255) NOT NULL DEFAULT '',

    *      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

    *       PRIMARY KEY (`id`)

    *   ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4;

   */

    require_once 'db.class.php';

    MySQL new new = $ \ MySQL (); 

    $ MySQL-> Connect (); 

    $ Data = [ 'ordersn' => $ order_sn, 'Status' => 0,' CreateTime '=> DATE (' Ymd H: I: S ', Time ())]; 

    $ MySQL-> INSERT (' Order ', $ Data); 

} 

 

$ = List [order_sn $, $ use_mysql]; 

$ = Key The implode (': ', $ List); 

 

$ redis- > setex ($ key, 3, 'redis delayed task'); // 3 seconds after the callback 

 

 

 

$ test_del = false; // test whether there will be expired after the callback to delete the cache. RESULTS: No callback 

IF ($ test_del == to true) { 

    // SLEEP (1); 

    $ redis-> the Delete ($ order_sn); 

} 

 

echo $ order_sn; 

 

 

 

/ * 

 * Other key test will have callback, Results: callback 

 * $ = K 'Test'; 

 * $ redis2-> SET (K $, '100'); 

 * $ redis2-> The expire ($ K, 10);


  

psubscribe.php

<PHP? 

the ini_set ( 'default_socket_timeout', -1); // not time out 

require_once 'Redis2.class.php'; 

$ redis_db = '15'; 

$ = Redis new new \ Redis2 ( '127.0.0.1', '6379', '', $ redis_db); 

// solve Redis client subscription time-out situations 

$ redis-> setOption (); 

// when key expired see the notification, the subscription key __keyevent @ <db> __: expired this format is fixed, db represents the number of the database, since after all the key to open the subscription expiration date will be pushed up this library, so it is best to use a single database to isolate 

$ redis-> psubscribe (array ( ' __ keyevent @' . $ redis_db .'__: expired The '),' keyCallback '); 

// callback function, where the processing logic writing 

function keyCallback (Redis $, $ pattern, Channel $, $ MSG) 

{ 

    echo value is PHP_EOL; 

    echo "the pattern: $ pattern \ the n-"; 

    echo" Channel: $ Channel \ the n-"; 

    echo"Payload: $msg\n\n";

    $list = explode(':',$msg);

 

    $order_sn = isset($list[0])?$list[0]:'0';

    $use_mysql = isset($list[1])?$list[1]:'0';

 

    if($use_mysql == 1){

        require_once 'db.class.php';

        $mysql = new \mysql();

        $mysql->connect();

        $where = "ordersn = '".$order_sn."'";

        $mysql->select('order','',$where);

        $finds=$mysql->fetchAll();

        print_r($finds);

        if(isset($finds[0]['status']) && $finds[0]['status']==0){

            $data   = array('status' => 3);

            $where  = " id = ".$finds[0]['id'];

            $mysql->update('order',$data,WHERE $); 
// or
}
    }

        }


 


 

 


/*$redis->psubscribe(array('__keyevent@'.$redis_db.'__:expired'), function ($redis, $pattern, $channel, $msg){

    echo PHP_EOL;

    echo "Pattern: $pattern\n";

    echo "Channel: $channel\n";

    echo "Payload: $msg\n\n";

    //................

});*/

  

Redis2.class.php

<?php

 

class Redis2

{

    private $redis;

 

    public function __construct($host = '127.0.0.1', $port = '6379',$password = '',$db = '15')

    {

        $this->redis = new Redis();

        $this->redis->connect($host, $port);    //连接Redis

        $this->redis->auth($password);      //密码验证

        $this->redis->select($db);    //选择数据库

    }

 

    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);

    }

 

}

  

window system test method: first in the cmd command interface runs psubscribe.php, then the page opens index.php.

The monitor background is always running (subscription)

There is a problem to do this step by phpredis expansion, the successful implementation of the listener expired Key in the code, and the callback processing psCallback () inside. Two requirements set forth at the beginning has been achieved. But here there is a problem: redis after performing a subscription operation, the terminal enters the blocked state, the need has been hanging in there. This script requires human and subscription in the command line, do not meet the actual demand.

In fact, we listen to expire callback demand, it is hoped the same as the daemon running in the background when the event has expired messages, trigger callback function. The listener want to always run as a background daemon is much the same as in the background,

I is accomplished.

Linux has a nohup command. Just do not hang up function to run the command. Meanwhile nohup output all scripts, have put nohup.out files in the current directory, if the file is not writable, then into the <user home directory> /nohup.out file. So with this command later, regardless of whether we close the terminal window, you can let the php script has been run.

Write psubscribe.php file:

? <PHP 

# / usr / bin / env PHP! 

ini_set ( 'default_socket_timeout', -1); // do not timeout 

require_once 'Redis2.class.php'; 

$ redis_db = '15'; 

$ Redis = new new \ Redis2 ( ' 127.0.0.1 ',' 6379 ',' ', $ redis_db); 

// solve Redis client subscription time-out situations 

$ redis-> setOption (); 

// when key expired to see notifications subscription key __keyevent @ <db> __: expired this format is fixed, db represents the number of the database, since after all the key to open the subscription expiration date will be pushed up this library, so it is best to use a single database to isolate 

$ redis- > psubscribe (. Array ( '__ KeyEvent @' $ redis_db .'__: expired The '),' keyCallback '); 

// callback function, where the processing logic writing 

function keyCallback (Redis $, $ pattern, Channel $, $ MSG) 

{ 

    PHP_EOL echo; 

    echo "pattern: $ pattern \ the n-"; 

    echo "Channel: $ Channel \ the n-";

    echo "Payload: $msg\n\n";

    $list = explode(':',$msg);

 

    $order_sn = isset($list[0])?$list[0]:'0';

    $use_mysql = isset($list[1])?$list[1]:'0';

 

    if($use_mysql == 1){

        require_once 'db.class.php';

        $mysql = new \mysql();

        $mysql->connect();

        $where = "ordersn = '".$order_sn."'";

        $mysql->select('order','',$where);

        $finds=$mysql->fetchAll();

        print_r($finds);

        if(isset($finds[0]['status']) && $finds[0]['status']==0){

            $data   = array('status' => 3);

            $where  = " id = ".$finds[0]['id'];

            $mysql->update('order',$data,$where);

        }

    }

 

}

 

 

//或者

/*$redis->psubscribe(array('__keyevent@'.$redis_db.'__:expired'), function ($redis, $pattern, $channel, $msg){

    echo PHP_EOL;

    echo "Pattern: $pattern\n";

    echo "Channel: $channel\n";

    echo "Payload: $msg\n\n";

    //................

});*/

  

Note: We are at the beginning, affirmed path php compiler:

#! /usr/bin/env php

  

This is necessary to execute php script.

Then, nohup does not suspend the execution psubscribe.php, pay attention to the end of the &

[root@chokingwin HiGirl]# nohup ./psubscribe.php & 

[1] 4456 nohup: ignoring input and appending output to `nohup.out'

  

Description: The script has indeed been run up on the process ID of 4456.

View the next nohup.out cat look nohuo.out, look if there is expired output:

[root@chokingwin HiGirl]# cat nohup.out 

Pattern:__keyevent@0__:expired 

Channel: __keyevent@0__:expired 

Payload: name

  

Run index.php, i.e. above 3 seconds after successful results

Encounter problems: using the command line mode on monitoring scripts, some time after being given:. Error while sending QUERY packet PID = xxx

Solution: Since a long wait is connected to the message queue, and wait before the callback connection database, the database wait_timeout = 28800, so long as the next message from the message more than 8 hours, this error will, provided the wait_timeout 10, and catch the exception, find the real error is MySQL server has gone away,
so long as the business logic processes all take the initiative to close the database connection, the database connection that is active close off the problem can be solved

yii solution is as follows:

Yii::$app->db->close();

  

View Process method:

ps -aux | grep psubscribe.php

  

a:显示所有程序 u:以用户为主的格式来显示 x:显示所有程序,不以终端机来区分

View jobs Process ID: [jobs -l] command

www@iZ232eoxo41Z:~/tinywan $ jobs -l
[1]-  1365 Stopped (tty output)    sudo nohup psubscribe.php > /dev/null 2>&1 
[2]+ 1370 Stopped (tty output) sudo nohup psubscribe.php > /dev/null 2>&1

  

 

Terminate processes running in the background method:

kill -9 process ID

  

 

Empty nohup.out file method:

cat /dev/null > nohup.out

  

 

We use nohup time, and are generally used in conjunction with &, but in actual use, many people put the program back on this matter, but in fact this is possible when the current account non-normal exit or end, or your own command ended.

So after running in the background using nohup command order, we need to do the following:

1. First enter, exit prompted the nohup.

2. Then perform normal exit to exit the current account.
3. Then go to the link terminal. Background makes the program running.

We should always use the exit to exit the terminal should not be closed immediately after each nohup executed successfully. So as to ensure the command has been run in the background.

These are the details to achieve delayed task of achieving automatic cancel orders (detailed tutorial) with PHP + Redis

Learn more information, please visit:

Tencent T3-T4 standard boutique Daquan PHP Architect tutorial directory, as long as you read the guarantee pay rises to a higher level (continually updated)

 

Guess you like

Origin www.cnblogs.com/a609251438/p/12667390.html