web-dev-qa-db-ja.com

サーバー送信イベントとPHP-サーバーでイベントをトリガーするものは何ですか?

すべて、

HTML5 Rocksには、サーバー送信イベント(SSE)に関する素敵な初心者向けチュートリアルがあります。

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

しかし、私は重要な概念を理解していません-メッセージが送信される原因となるサーバー上のイベントのトリガーは何ですか?

つまり、HTML5の例では、サーバーは単にタイムスタンプonceを送信します。

<?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関数を記述するのとどう違うのですか?

すみません-これはおそらく信じられないほど素朴な質問です。しかし、私が見つけることができた例のどれもこれを明確にしません。

[更新]

私の質問は言葉遣いが不十分だと思うので、ここにいくつかの説明があります。

Appleの在庫の最新の価格を表示するWebページがあるとしましょう。

ユーザーが最初にページを開くと、ページは「ストリーム」のURLでEventSourceを作成します。

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を実行しているスレッドと同じくらい多くのスレッドを持っていることを意味しますか?もしそうなら-それはリモートで実行可能ですか、それともアプリケーションを構築する適切な方法ですか?そして、いつstream.phpのインスタンスを[〜#〜] end [〜#〜]できるかをどのように知るのですか?

私の素朴な印象は、この場合、PHPはこの種のサーバーに適した技術ではないということです。しかし、私がこれまでに見てきたすべてのデモは、PHPがこれには問題ないことを暗示しています。だから私はとても混乱しています...

76
mattstuehler

サーバー送信イベントは、サーバー側からクライアント側へのリアルタイム更新用です。最初の例では、サーバーからの接続は保持されず、クライアントは3秒ごとに再度接続を試行し、サーバー送信イベントがajaxポーリングと変わらないようにします。

そのため、接続を持続させるには、コードをループでラップし、常に更新を確認する必要があります。

PHPはスレッドベースであり、より多くの接続ユーザーがサーバーのリソースを使い果たします。これは、スクリプトの実行時間を制御し、スクリプトが時間(10分)を超えたときにスクリプトを終了することで解決できます。 EventSource AP​​Iは自動的に再接続するため、遅延は許容範囲内になります。

また、私の サーバー送信イベント用のPHPライブラリー を確認してください。サーバー送信イベントを行う方法については、PHPでコードを簡単に作成する方法について詳しく理解できます。

28
Licson

「...クライアントが「stream.php」に「接続」されている限り、「stream.php」は開いたままですか?」

はい、あなたの擬似コードは合理的なアプローチです。

「そして、stream.phpのインスタンスを終了できる時期はどのようにしてわかりますか?」

最も典型的なケースでは、これはユーザーがサイトを離れたときに起こります。 (Apacheは閉じたソケットを認識し、PHPインスタンスを強制終了します。)サーバー側からソケットを閉じる主な時間は、しばらくの間データがなくなることがわかっている場合です。 ;クライアントに送信する最後のメッセージは、特定の時間に戻ってくるように伝えることです。たとえば、株式ストリーミングの場合、午後8時に接続を閉じ、8時間以内に戻ってくるようにクライアントに伝えます(NASDAQが開いていると仮定します)金曜日の夕方、月曜日の朝に戻ってくるように伝えます(SSEについての本がありますので、このテーマについていくつかのセクションを捧げます)。

「...この場合、PHPはこの種のサーバーに適したテクノロジーではありません。しかし、これまで見てきたすべてのデモは、PHPはこれで問題ないので、私はとても混乱しています...」

まあ、人々はPHPは通常のウェブサイトに適した技術ではない、と彼らは正しいです:あなたがLAMPスタック全体を置き換えれば、はるかに少ないメモリとCPUサイクルでそれを行うことができますC++。ただし、これにもかかわらず、PHPはほとんどのサイトで問題なく動作します。よく知られているCライクな構文などの組み合わせにより、Webワークにとって非常に生産的な言語です。多くのライブラリ、そして多くのPHP雇うプログラマ、たくさんの本や他のリソース、そしていくつかの大規模なユースケース(FacebookやWikipediaなど)としてのマネージャー向けの快適なライブラリ。これらは基本的にストリーミングテクノロジーとしてPHP.

典型的なセットアップは、PHPインスタンスごとに1つのNASDAQへの接続ではありません。代わりに、NASDAQへの単一の接続、またはクラスター内の各マシンからNASDAQへの単一の接続を持つ別のプロセスを作成します。その後、価格がSQL/NoSQLサーバーまたは共有メモリにプッシュされます。次にPHPはその共有メモリ(またはデータベース)をポーリングし、データをプッシュします。または、データ収集サーバーがあり、各PHPインスタンスが開きますそのサーバーへのソケット接続。データ収集サーバーは、PHPクライアントを受信すると、それらの各クライアントに更新をプッシュし、クライアントはそのデータをクライアントにプッシュアウトします。

ストリーミングにApache + PHPを使用する場合の主なスケーラビリティの問題は、各Apacheプロセスのメモリです。ハードウェアのメモリ制限に達したら、別のマシンをクラスターに追加するか、Apacheをループから切り離して専用のHTTPサーバーを作成するというビジネス上の決定を下します。後者はPHPで実行できるため、既存の知識とコードをすべて再利用できます。または、アプリケーション全体を別の言語で書き換えることができます。私の純粋な開発者は、 C++の合理化されたHTTPサーバー:私のマネージャーは別のボックスを追加します。

22
Darren Cook

私は、sse techinkがすべての遅延データをクライアントに送信することに気づきました(クライアントページe.x. Ajaxプーリングデータからプーリングデータtechinkを反転させるようなものです)。

_<?php
        session_start();
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache'); // recommended to prevent caching of event data
        require 'sse.php';
        if ($_POST['message'] != ""){
                $_SESSION['message'] = $_POST['message'];
                $_SESSION['serverTime'] = time();
        }
        sendMsg($_SESSION['serverTime'], $_SESSION['message'] );
?>
_

sse.phpは次のとおりです。

_<?php
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
?>
_

SseSerer.phpでセッションを開始し、セッション変数を使用していることに注意してください!問題を克服するために。

また、メッセージを「更新」するたびに、Ajax経由でsseServer.phpを呼び出します(投稿し、値を_variable message_に設定します)。

今jQuery(javascript)で私はそのようなことをします:1)グローバル変数を宣言しますvar timeStamp = 0; 2)次のアルゴリズムを使用します:

_if(typeof(EventSource)!=="undefined"){
        var source=new EventSource("sseServer.php");
        source.onmessage=function(event)
        if ((timeStamp!=event.lastEventId) && (timeStamp!=0)){
                /* this is initialization */
                timeStamp=event.lastEventId;
                $.notify("Please refresh "+event.data, "info");
        } else {
                if (timeStamp==0){
                         timeStamp=event.lastEventId;
                }
        } /* fi */

} else {
        document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
} /* fi */
_

$.notify("Please refresh "+event.data, "info");の行には、メッセージを処理できるものがあります。

私の場合、jQuery通知を送信していました。

代わりにPOSIX PIPESまたはDBテーブルを使用して、POSTを介して「メッセージ」を渡すことができます。sseServer.phpは「無限ループ」のように動作するためです。

当時の私の問題は、上記のコードがすべてのクライアントに「メッセージ」を送信するのではなく、ペア(sseServer.phpを呼び出したクライアントがすべてのペアに対して個別に機能する)のみに送信するため、technikと「メッセージ」をトリガーするページからDBを更新し、代わりにsseServer.phpを使用してPOSTを介してメッセージを取得します。DBテーブルから取得します。

私が助けてくれることを願っています!

4
c.chasapis

これは、アプリケーションに関する構造的な質問です。リアルタイムイベントは最初から考えたいことなので、その周りにアプリケーションを設計できます。文字列クエリを使用してランダムな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);

これは最も優雅な例ではありませんが、まともな構成要素として役立つはずです。実際の永続化レイヤーにフックして、これらのイベントのトリガーを処理できます。次に、サーバーにハンマーをかけることなく、すぐに(取得できる限りリアルタイムで)それらを取得します(データベースに絶えず問い合わせて、変更があったかどうかを確認する必要がないため)。

データベースへの手動の変更をこの方法でキャッチすることは明らかではありませんが、データベースに対して何らかの頻度で手動で変更を行う場合は、次のいずれかを行う必要があります。

  • 手動で変更する必要がある問題を修正する
  • プロセスを促進するツールを作成し、これらのイベントを起動します
3
Colin M