while(true)應用之 實現本身的消息隊列

  早些時候,一直有個疑問,就是好比你從前端發一個操做以後,後臺爲何可以及時處理你的東西呢?固然了,我說的不是,服務器爲何可以當即接收到你的請求之類高大上的東西。而是,假設你用異步去作一個事情,然後臺有一個處理程序在處理你的申請,你的目的天然是不想讓操做阻塞,因此處理確定是處理程序主動觸發的過程。那麼怎樣作可以讓其可以及時處理問題呢? 確實也困惑了我許久。(我相信也有很多同窗會有相似疑問)php

  因此我以爲有必要解解惑。前端

  需求示例1: 用戶請求某操做時,須要與此同時給用戶發個郵件,可是這個郵件可能會很耗時,在不使用異步線程的狀況下(事實上不是全部的語言都支持多線程),怎麼樣讓用戶獲得快速響應,且郵件稍後便可送達?redis

  需求示例2:在高併發狀況下,須要對某數據進行減操做(好比商品庫存,若是超出了庫存值,則將無貨可發),怎樣保證用戶先到先得,後到就沒有?服務器

  針對這兩個問題,解決辦法天然是多的。可是本文只說一個思路,那就是主題,消息隊列!多線程

    當須要給用戶發送郵件時,只需將該請求發送到後臺進程中,後臺進程進行逐個發送便可。併發

    當大量用戶進來搶商品時,將該請求放入隊列中,後臺進程逐個相減,減到0時,後續用戶將提示搶不到了。(固然,這有不少後續問題要處理,也看起來不必定是個好方案,但並不影響我們發揮)異步

  看起來,前面的解決方案很合理。可是,具體怎麼樣作呢?前臺申請,與後臺處理之間,總得有個什麼東西聯繫起來吧。沒錯,就是消息隊列了。消息隊列天然須要消息中間件,簡單的,我們就使用redis作中間件吧,簡單快速搞得定。高併發

  具體實施方案就是:1. 各處的用戶進行相應的操做請求,而後順便將消息寫入redis,(以list形式寫入,自然的隊列); 2. 後臺進程依次從redis中讀取消息,進行相應數據處理(注意如何依次處理是關鍵)。 3. 將結果通知給用戶或者不通知。(本處將不通知)spa

  示例代碼以下(php實現):命令行

<?php
// send 請求方,寫入消息
$redis = new Redis();
$redis->connect("127.0.0.1", "6379", 3);

$msgKey = "my.test.msgKey";
$value = "hello,world." . rand(0, 99999999);
$redis->lPush($msgKey, $value);           // 將請求送入隊列中,等後臺消費
echo "lPush {$msgKey} -> {$value}";

  後臺進程進行依次處理,通常來講有兩個方案: 1. 經過系統進行定時調度,每次調度,則執行一段消息處理(此種方案的缺點明顯,需依賴系統處理,且將會是不及時的); 2. 經過自身調度,使本身一直處理運行中狀態,當發現有新消息到來時,當即進行處理(本處討論的是此種方案)。處理代碼以下:

<?php
// recieve 處理程序
$redis = new Redis();
$redis->connect("127.0.0.1", "6379", 3);

$msgKey = "my.test.msgKey";

while(true) {
    $stackTop = $redis->rPop($msgKey);
    if($stackTop) {
        // do sth useful
        echo $stackTop . "\r\n";
    } else {
        usleep(200000);         // 若是沒有消息須要處理,則睡眠0.2秒等待
    }
}

  寫好後,只要在命令行將該腳本跑起來便可:

php  recieve.php &

  其實原理很簡單,就是一個while死循環,而後一直在查詢 消息狀態,有就處理,沒有就稍微等一下再查。

  若是啓動多個後臺程序,那麼,就至關於有多個消費者了,從而加快了處理速度。(生產者 -> 消費者 簡單模型)

  那麼,問題在哪裏?爲何剛開始的時候沒想到這樣處理呢?

  我有兩個疑問:

     1. 用戶while死循環不會致使死機嗎?

     2. 我想修改代碼裏的東西,怎樣才能生效?

  針對第一個問題,若是是沒有 sleep 限制的話,機器是有可能死機的。在調用 sleep後,cpu會轉到其餘進程上進行事務處理,從而不會有問題。sleep時間過長,則會有明顯的時間停頓現象,即用戶操做沒法獲得及時響應。sleep時間太短,則會致使cpu佔用太高,從而引起其餘一系列問題。所以設置一個合適的sleep時間是有必要的。本處設置的0.2秒,經查看cpu狀態,佔用爲0%,因此沒問題。並且0.2秒在用戶看來,是沒有什麼影響的。

  第二個問題,修改了代碼,如何生效?重啓就行了嘛。

ps -ef | grep php      # 找出運行代碼的pid
kill -9 123            # 將進程kill掉
php recieve.php        # 從新運行代碼便可

  經過該種自身輪詢的方式,從而達到了及時處理任務的方式。 

  死循環普遍應用於各服務中,只是咱們都沒發現。

  這也換一個現實的問題角度理解,只有本身一直活着,纔有可能服務於別人。

  那麼,假如每一個程序跑起來後,都一直存活着,CPU不就完蛋了? 是的,這就是計算機所能運行的服務有限的緣由。CPU能夠調度各進程的執行,固然進程數是有限的,只要在這有限有量之內,提供幾個死循環仍是能夠的。(注意,死循環是保持自身活躍的一種方式,但並不是全部的服務都是靠死循環來保持自身的活躍的)

  信號量?是一種有效地處理本機通知的一種機制,且聽下回分解。

相關文章
相關標籤/搜索