1. 程式人生 > >PHP使用Redis實現消息隊列

PHP使用Redis實現消息隊列

.com 正在 res mys ++ 簡單的 root order 訪問

消息隊列可以使用MySQL來實現,可以參考博客PHP使用MySQL實現消息隊列,雖然用MySQL可以實現,但是一般不這麽用,因為MySQL的數據都存在硬盤中,而從硬盤中對MySQL的操作,I/O花費的代價很大,所以一般使用緩存來實現,因為緩存的數據是在內存中,訪問內存的速度遠快於訪問硬盤的速度。另一方面,Redis有list類型的數據結構,非常適合做消息隊列。

這裏舉一個很簡單的秒殺例子:秒殺的名額只有5個,即消息隊列的長度為5,名額已經滿了之後,通知後來的人已經秒殺結束。然後後臺會從消息隊列中讀取數據,然後將數據存到數據庫中。因為消息隊列長度只有5個,而且秒殺的那短短1,2秒並沒有直接操作數據庫,所以對於數據庫來說,並沒有什麽壓力。

先看一下數據庫表(seckill)的結構:

mysql> desc seckill;
+----------+---------+------+-----+---------+----------------+
| Field    | Type    | Null | Key | Default | Extra          |
+----------+---------+------+-----+---------+----------------+
| id       | int(11) | NO   | PRI | NULL    | auto_increment |
| order_id | int(11) | NO   |     | NULL    |                |
| mobile   | int(8)  | YES  |     | 8888888 |                |
+----------+---------+------+-----+---------+----------------+
3 rows in set (0.11 sec)

  然後是進行秒殺的用戶程序(user.php),為了模擬,這裏使用for循環來實現在短時間內發起大量的請求,但是要知道這是不準確的。

<?php
    $redis=new Redis();
    $redis->connect("127.0.0.1",6379);

    $key="seckill";
    for($i=0;$i<10;$i++){
        $order_id=rand(100000,999999);
        $mobile=rand(11111111,99999999);
        $value=$order_id."#".$mobile;//連接之後作為值
        if($redis->llen("seckill") <5 ){
            echo "秒殺成功,訂單號為$order_id, 手機號為$mobile\n";
            $redis->lpush($key,$value);
        } else {
            echo "秒殺已經結束\n";
        }
    }
?>

  運行結果:

[root@localhost ~]# php user.php
秒殺成功,訂單號為643275, 手機號為50104929
秒殺成功,訂單號為393012, 手機號為31213041
秒殺成功,訂單號為994790, 手機號為23107569
秒殺成功,訂單號為186135, 手機號為36549273
秒殺成功,訂單號為821972, 手機號為11217760
秒殺已經結束
秒殺已經結束
秒殺已經結束
秒殺已經結束
秒殺已經結束

  

然後是後臺程序將redis中訂單讀出,處理後存進數據庫。

<?php
    $redis=new Redis();
    $redis->connect("127.0.0.1",6379);
    $pdo=new PDO("mysql:host=localhost;dbname=test","root","root");
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $stmt=$pdo->prepare("insert into seckill(id,order_id,mobile) values(?,?,?)");
    $key="seckill";

    while($redis->llen($key)){
        //因為消息隊列中添加消息是使用lpush,所以這裏使用rpop
        $order=$redis->rpop($key);
        list($order_id,$mobile)=explode("#",$order);
        echo "正在處理訂單$order_id\t";
        try{
            $res=$stmt->execute(array(null,$order_id,$mobile));
            if(!$res){
                throw new PDOException("wrong");
            }
        } catch (PDOException $e){
            echo $e->getMessage();
            echo "訂單處理失敗\n";
            $redis->rpush($key,$order_id."#".$mobile);//將數據恢復達到隊列中
            continue;
        }
        echo "訂單處理完成\n";
    }
?>

  運行:

[root@localhost ~]# php consumer.php
正在處理訂單643275      訂單處理完成
正在處理訂單393012      訂單處理完成
正在處理訂單994790      訂單處理完成
正在處理訂單186135      訂單處理完成
正在處理訂單821972      訂單處理完成

  查看數據庫:

mysql> select * from seckill;
+----+----------+----------+
| id | order_id | mobile   |
+----+----------+----------+
|  1 |   643275 | 50104929 |
|  2 |   393012 | 31213041 |
|  3 |   994790 | 23107569 |
|  4 |   186135 | 36549273 |
|  5 |   821972 | 11217760 |
+----+----------+----------+
5 rows in set (0.00 sec)

  

PHP使用Redis實現消息隊列