在nginx下利用php配置SSE的正確方法

SSE簡介

SSEServer-Sent Events的縮寫。一般狀況下,是咱們的瀏覽器向服務器發起請求後,服務器響應,而後關閉鏈接。爲了可以保持通訊,以便在服務器有事件發生時主動通知瀏覽器,後來人們又發明了不少技術,包括websocket等。可是websocket對於代碼改動較大,因此又出現了SSE,它的特色是基本不用改寫原有的邏輯,只是增長一些小的改動就能實現服務器與客戶端之間的長鏈接,達到服務器主動通知客戶端的目的。javascript

可是我在按照網上教程真正去在nginx環境下實現SSE時,頗費了一番周章,留在這裏,以便有同窗遇到相似問題時參考。php

客戶端

SSE的主要原理是由客戶端,也就是瀏覽器裏的javascript發起一個相似於ajax的請求,但和ajax不一樣的是,這是一個一直保持的長鏈接,一旦請求創建以後,客戶端開始安靜地等待服務端向它發回數據,這個鏈接能夠保持很長很長時間。java

因此客戶端的代碼很簡單:nginx

source = new EventSource('http://api.server.com/path/file.php?param=value');
source.onmessage = function (event) {
    json = JSON.parse(event.data);
    ... 後面是你處理數據的部分 ...
};

不用擔憂斷掉,即便斷掉的話,客戶端會在斷掉以後的3秒以後自動再次從新向服務端發起鏈接請求,並且這個從新鏈接是瀏覽器自動幫助咱們實現的,咱們在編程時能夠徹底不去考慮它。web

服務器端

php部分

header('Access-Control-Allow-Origin: http://www.server.com');
//發送SSE應答
header('X-Accel-Buffering: no');
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$old_md5 = '';
//執行100次,每次睡眠3秒鐘,總共300秒,也就是5分鐘
for ($i=0; $i<100; $i++) {
    //保持一個長鏈接,每隔3秒鐘回答客戶端一次
    $o_data = 你的數據;
    $str_return = json_encode($o_data);
    $md5 = md5($str_return);
    if ($md5 != $old_md5) {
        //若是內容發生了變化,則推送,不然沒必要推送,以節省網絡流量
        echo 'data: ' . $str_return . "\n\n";
        $old_md5 = $md5;
    }
    ob_flush();
    flush();
    //等待3秒鐘,開始下一次查詢
    sleep(3);
}

逐行分解一下:ajax

這三句話必不可少:編程

header('X-Accel-Buffering: no');
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

其中的第一句話,是配置nginx必需的。由於nginx缺省會對全部來自php的數據做緩存,它必定要等到全部數據所有寫進緩存後才一股腦發給客戶端,而咱們要創建的是一個很長的鏈接,這個鏈接過程當中隨時可能要發送數據,不須要緩存,因此這裏必須經過X-Accel-Buffering告訴nginx你對我這段代碼不要給我作緩存,不然你會在客戶端等很長很長時間卻一個字符也收不到。json

注意後面那個100次的for循環,網上幾乎全部的例子裏在這裏都會使用while(1)的一個無限循環,但我我的的經驗偏偏相反,在這裏應該是一個for循環,而不是while循環。爲何呢?由於咱們必須使得整個程序的執行時間可控,若是你用for循環100次,每次sleep 3秒鐘的話,整個腳本的執行時長也就是在5分鐘左右,而若是你使用while無限循環的話,整個時間會幾乎是無限長。有的同窗可能會說我在php.ini裏設置了max_excution_time60秒鐘啊,很好,我一開始也是這麼認爲的,可是:sleep(3)的時間不算在內!因此你能夠想象一下你的腳本要執行多長時間纔會結束並安靜地從內存中退出去?api

nginx設置

最後,還須要配置nginx,只須要一句話,在你的nginx.conflocation裏:瀏覽器

fastcgi_read_timeout 600s;

這是因爲nginx在和咱們的php-fpm進行通信的時候,這個地方的缺省值是60,若是它等了60秒,結果php-fpm一個字符也沒有送過來的話,nginx會強行停止與咱們的程序的鏈接,但咱們的程序實際尚未執行結束,這時候js客戶端發現鏈接斷了,就會自動重啓一個新的鏈接,結果咱們服務器端的資源很快被耗盡了。在這裏設爲600秒的意思是告訴nginx,即便個人php代碼一個字符也不給你發送,你也必須老老實實呆夠10分鐘才能夠退出。而其實是,還記得咱們前面的for循環嗎?咱們會在100次等待3秒後,也就是5分鐘內退出咱們的php腳本執行,不會湊夠10分鐘的。因此這裏很安全。

相關文章
相關標籤/搜索