php - sent - w3school storage




服務器發送的事件和php-什麼觸發服務器上的事件? (4)

所有,

HTML5 Rocks有一個很好的關於服務器發送事件(SSE)的初學者教程:

http://www.html5rocks.com/en/tutorials/eventsource/basics/

但是,我不明白一個重要的概念 - 是什麼觸發了服務器上導致消息發送的事件?

換句話說 - 在HTML5示例中 - 服務器只發送一次時間戳:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));

如果我正在構建一個實際的例子 - 例如,Facebook風格的“牆”或股票代碼,其中服務器每當某些數據發生變化時會向客戶端“推送”新消息,那麼它是如何工作的?

換句話說...... PHP腳本是否有一個連續運行的循環,檢查數據的變化,然後每次發現一個消息時發送一條消息? 如果是這樣 - 你怎麼知道何時結束這個過程?

或者 - PHP腳本是否只是發送消息,然後結束(如HTML5Rocks示例中的情況)? 如果是這樣 - 你如何獲得持續更新? 瀏覽器是否只是定期輪詢PHP頁面? 如果是這樣 - 那是一個“服務器發送的事件”? 這與使用AJAX定期調用PHP頁面的JavaScript中編寫setInterval函數有什麼不同?

對不起 - 這可能是一個非常天真的問題。 但是我找不到的例子都沒有說清楚。

[UPDATE]

我認為我的問題措辭不多,所以這裡有一些澄清。

假設我有一個網頁,應該顯示Apple股票的最新價格。

當用戶首次打開頁面時,該頁面會創建一個EventSource,其URL為我的“stream”。

var source = new EventSource('stream.php');

我的問題是 - “stream.php”應該如何工作?

喜歡這個? (偽代碼):

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
    function sendMsg($msg) {
        echo "data: $msg" . PHP_EOL;
        echo PHP_EOL;
        flush();
    }

    while (some condition) {
        // check whether Apple's stock price has changed
        // e.g., by querying a database, or calling a web service
        // if it HAS changed, sendMsg with new price to client
        // otherwise, do nothing (until next loop)
        sleep (n) // wait n seconds until checking again
    }
?>

換句話說 - 只要客戶端“連接”它,“stream.php”是否保持打開狀態?

如果是這樣的話 - 這是否意味著您擁有與並髮用戶一樣多的運行stream.php線程? 如果是這樣 - 是遠程可行的,還是構建應用程序的適當方式? 你怎麼知道何時可以結束 stream.php一個實例?

我的天真印像是,如果是這種情況,PHP 適合這種服務器。 但到目前為止我看到的所有演示都暗示PHP對此很好,這就是為什麼我很困惑......


“......只要客戶”與“連接”,“stream.php”會保持打開狀態嗎?“

是的,你的偽代碼是一種合理的方法。

“你怎麼知道何時可以結束stream.php的實例?”

在最典型的情況下,當用戶離開您的站點時會發生這種情況。 (Apache識別關閉的套接字,並殺死PHP實例。)從服務器端關閉套接字的主要時間是你知道一段時間內沒有數據; 您發送給客戶的最後一條消息是告訴他們在某個時間回來。 例如,在您的股票流媒體案例中,您可以在晚上8點關閉連接,並告訴客戶在8小時內回來(假設NASDAQ從凌晨4點到晚上8點開放報價)。 星期五晚上你告訴他們星期一早上回來。 (我有一本關於SSE的新書,並在這個主題上專門介紹了幾個部分。)

“...如果是這種情況,PHP對於這種服務器來說不是一種合適的技術。但到目前為止我看到的所有演示都暗示PHP對此很好,這就是為什麼我這樣做的原因困惑...”

好吧,人們認為PHP不適合普通的網站,而且它們是正確的:如果用C ++替換整個LAMP堆棧,你可以用更少的內存和CPU週期來完成它。 然而,儘管如此,PHP還是為大多數網站提供了很好的功能。 它是一種非常高效的Web工作語言,因為它結合了熟悉的類C語法和如此多的庫,並且對於管理人員而言是一種令人欣慰的語言,因為大量的PHP程序員可以僱用,有大量的書籍和其他資源,還有一些大的用例(例如Facebook和維基百科)。 這些與您選擇PHP作為流媒體技術的原因基本相同。

典型的設置不是每個PHP實例與NASDAQ的一個連接。 相反,您將擁有另一個與NASDAQ連接的進程,或者可能是從群集中的每台計算機到NASDAQ的單個連接。 然後將價格推入SQL / NoSQL服務器或共享內存。 然後,PHP只會輪詢共享內存(或數據庫),並將數據推出。 或者,擁有一個數據收集服務器,每個PHP實例打開一個到該服務器的套接字連接。 數據收集服務器在收到每個PHP客戶端時會推送更新,然後他們將這些數據推送到客戶端。

使用Apache + PHP進行流式處理的主要可伸縮性問題是每個Apache進程的內存。 當您達到硬件的內存限制時,做出業務決策,將另一台計算機添加到集群,或者將Apache從循環中刪除,並編寫專用的HTTP服務器。 後者可以用PHP完成,因此您可以重用所有現有知識和代碼,或者您可以用另一種語言重寫整個應用程序。 我的純開發人員將用C ++編寫一個專用的,簡化的HTTP服務器。 我的經理會添加另一個盒子。


基本上,PHP不適用於此類事物。 是的,你可以使它工作,但它將是一個災難高負荷。 我們運行通過websockets將庫存變化信號發送到數十個用戶的股票服務器 - 如果我們使用php那麼......好吧,我們可以,但那些自製週期 - 只是一場噩夢。 每個連接都將在服務器上創建一個單獨的進程,或者您必須處理來自某種數據庫的連接。

只需使用nodejs和socket.io即可。 它可以讓您輕鬆啟動並在幾天內運行服務器。 Nodejs也有自己的局限性,但對於websockets(和SSE)連接,它現在是最強大的技術。

而且 - SSE並不像看起來那麼好。 websockets的唯一優勢 - 數據包是本機gzip(ws不是gzip),但缺點是SSE是單端連接。 您的用戶,如果他想將另一個股票代碼添加到子標籤,則必鬚髮出ajax請求(包括原始控制的所有問題,請求將會很慢)。 在websockets中,客戶端和服務器在一個單獨的打開連接中進行雙向通信,因此如果用戶發送交易信號或訂閱報價,他只需在已打開的連接中發送一個字符串。 它很快。


服務器發送的事件用於從服務器端到客戶端的實時更新。 在第一個示例中,不保留來自服務器的連接,並且客戶端每3秒嘗試再次連接,並使服務器發送的事件與ajax輪詢沒有區別。

因此,要使連接保持不變,您需要將代碼包裝在循環中並不斷檢查更新。

PHP是基於線程的,更多連接的用戶將使服務器耗盡資源。 這可以通過控制腳本執行時間來解決,並在腳本超過一定時間(即10分鐘)時結束腳本。 EventSource API將再次自動連接,因此延遲在可接受的範圍內。

另外,查看我的PHP庫以獲取服務器發送的事件 ,您可以了解有關如何在PHP中執行服務器發送的事件以及使代碼更容易理解的更多信息。


這實際上是關於您的應用程序的結構性問題。 實時事件是您從一開始就想要考慮的事情,因此您可以圍繞它設計應用程序。 如果您編寫的應用程序只運行一堆隨機的mysql(i)_query方法,使用字符串查詢並且不通過任何類型的中介傳遞它們,那麼很多時候您將無法選擇但是要么重寫您的應用程序,或進行持續的服務器端輪詢。

但是,如果您將實體作為對象進行管理並通過某種中間類傳遞它們,則可以掛鉤該過程。 看看這個例子:

<?php
class MyQueryManager {
    public function find($myObject, $objectId) {
        // Issue a select query against the database to get this object
    }

    public function save($myObject) {
        // Issue a query that saves the object to the database
        // Fire a new "save" event for the type of object passed to this method
    }

    public function delete($myObject) {
        // Fire a "delete" event for the type of object
    }
}

在您的應用程序中,當您準備保存時:

<?php
$someObject = $queryManager->find("MyObjectName", 1);
$someObject->setDateTimeUpdated(time());
$queryManager->save($someObject);

這不是最優雅的例子,但它應該作為一個體面的構建塊。 您可以掛鉤到實際的持久層以處理觸發這些事件。 然後你可以立即得到它們(盡可能實時)而無需錘擊你的服務器(因為你不需要經常查詢你的數據庫,看看是否有變化)。

您顯然不會以這種方式捕獲對數據庫的手動更改 - 但如果您以任何頻率手動對數據庫執行任何操作,則應該:

  • 解決需要您進行手動更改的問題
  • 構建一個工具來加速流程,並解僱這些事件




server-sent-events