SSE
是Server-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
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_time
是60
秒鐘啊,很好,我一開始也是這麼認爲的,可是:sleep(3)的時間不算在內!因此你能夠想象一下你的腳本要執行多長時間纔會結束並安靜地從內存中退出去?api
最後,還須要配置nginx
,只須要一句話,在你的nginx.conf
的location
裏:瀏覽器
fastcgi_read_timeout 600s;
這是因爲nginx
在和咱們的php-fpm
進行通信的時候,這個地方的缺省值是60
,若是它等了60
秒,結果php-fpm
一個字符也沒有送過來的話,nginx
會強行停止與咱們的程序的鏈接,但咱們的程序實際尚未執行結束,這時候js
客戶端發現鏈接斷了,就會自動重啓一個新的鏈接,結果咱們服務器端的資源很快被耗盡了。在這裏設爲600
秒的意思是告訴nginx
,即便個人php
代碼一個字符也不給你發送,你也必須老老實實呆夠10
分鐘才能夠退出。而其實是,還記得咱們前面的for
循環嗎?咱們會在100
次等待3
秒後,也就是5
分鐘內退出咱們的php
腳本執行,不會湊夠10
分鐘的。因此這裏很安全。