2017-06-05 55 views
0

我正在更新一個呈現實時傳感器數據的Web應用程序,它在當前的第一次迭代中是通過連續的AJAX輪詢實現的。 但是,爲了讓這更像是一個'真正的實時'應用程序,我希望它是基於事件的。實時傳感器數據作爲從Python到PHP的服務器發送事件(SSE)?

我一直在閱讀基於事件的技術,並且基於實時通信只需要一種方式(服務器 - >客戶端)的事實,我選擇使用Server-Sent Events (SSE),而不是像websockets。 如上所述here on the Mozilla Docs,這是很容易與像在服務器端執行(有點簡化):

<?php 
// SSEscript.php 
date_default_timezone_set("America/New_York"); 
header("Content-Type: text/event-stream\n\n"); 

while (1) { 
    if ($new_data_available) { 
     echo "data:". $data; 
    } 

    sleep($short_time_to_spare_cpu); 
} 
?> 

,並在客戶端使用:

<script> 
var evtSource = new EventSource("SSEscript.php"); 

evtSource.onmessage = function(e) { 
    var data = e.data; 
    // Do something with data object 
} 
</script> 

上述所有工作正常,我。但是,傳感器數據最初是通過在服務器上連續運行的Python腳本檢索的,因此,如何在檢索時將傳感器數據從Python腳本立即傳輸到PHP腳本,以便可以生成併發送事件?

我可以做類似下面描述的事嗎? : scenario1

同時所有新數據都存儲在一個MySQL數據庫中,所以我當然可以讓PHP腳本經常查詢數據庫以查找新條目,但必須有一個更智能的方法。那麼我可以讓圖像中的2.1和2.2在同一時間發生嗎?

我在這裏可以找到的所有答案都描述瞭如何通過讓PHP執行Python腳本來傳輸數據,但這不是我想要的,因爲無論用戶是否要求數據,這都會運行。

是一種套路要走的路,如果是這樣,你能指出我該怎麼做的方向嗎? 我希望你能幫助我!

回答

1

我使用redis做python和redis之間的信號傳輸。在python完成所有的工作之後,將一個令牌(或最新數據)發送到redis隊列中。在PHP中,我使用一個while(true)循環來保存請求和redis lpop(隊列,超時)等待令牌,併發送數據,如下所示:

<?php 
    require __DIR__.'/predis-1.0/autoload.php'; 
    header("Content-Type: text/event-stream"); 
    header("Cache-Control: no-cache"); 
    header("Connection: keep-alive"); 

    $lastId = isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? 
       $_SERVER["HTTP_LAST_EVENT_ID"] : null; 
    if (isset($lastId) && !empty($lastId) && is_numeric($lastId)) { 
     $lastId = intval($lastId); 
     $lastId++; 
    } 

    $index = isset($_GET['index']) ? $_GET['index'] : null; 

    echo "retry: 2000\n"; 

    $client = new Predis\Client(); 
    while (true) { 
     $data = $client->blpop('queue',5); 
     if ($data) { 
      error_log("$index : " . strlen($data)); 
      sendMessage($lastId, $data); 
      $lastId++; 
     } 
    } 

    function sendMessage($id, $data) { 
     echo "id: $id\n"; 
     echo "data: $data\n\n"; 
     ob_flush(); 
     flush(); 
    } 
+0

感謝您的回答!我不知道Redis,但經過一些研究後,它確實看起來是一個很好的解決方案。然而,根據文檔,不應該是超時工作而是'blpop'而不是'lpop'? (https://redis.io/commands/blpop)。另外,你可以解釋HTTP_LAST_EVENT_ID部分是如何工作的嗎? 'echo'重試的功能是什麼:2000 \ n「;'? –

+0

感謝您指出和是blpop(固定)。如果您閱讀SSE的詳細信息,如果瀏覽器斷開連接並自動重新連接,它將通過HTTP_LAST_EVENT_ID通過頭和php發送最後一個事件ID,這允許服務器端發送斷開連接期間可用的數據。 「retry:2000 \ n」告訴瀏覽器在2000毫秒後自動重新連接(保持網絡穩定,避免斷開總是比快速重新連接好) –

+0

對最後一個事件ID有意義,謝謝!重試功能是否需要任何附加代碼,或者是否使用EventSource接口調用? –

相關問題