109

全て、

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

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

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

言い換えると、HTML5の例では、サーバーはタイムスタンプを1回送信するだけです。

<?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ですか?

私の素朴な印象は、これが事実である場合、PHPはこの種のサーバーに適したテクノロジーではないということです。しかし、これまでに見たすべてのデモは、PHPがこれに適していることを示しています。そのため、私は非常に混乱しています...

4

5 に答える 5

35

サーバー送信イベントは、サーバー側からクライアント側へのリアルタイム更新用です。最初の例では、サーバーからの接続は維持されず、クライアントは3秒ごとに再接続を試み、サーバーから送信されたイベントをajaxポーリングと区別しません。

したがって、接続を維持するには、コードをループでラップし、更新を常にチェックする必要があります。

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

また、私のPHPライブラリでサーバー送信イベントを確認してください。PHPでサーバー送信イベントを実行する方法を理解し、コーディングを容易にすることができます。

于 2013-02-12T01:50:54.990 に答える
33

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

はい、疑似コードは合理的なアプローチです。

「では、stream.php のインスタンスを END できるタイミングをどのように知ることができますか?」

最も典型的なケースでは、これはユーザーがサイトを離れたときに発生します。(Apache は閉じられたソケットを認識し、PHP インスタンスを強制終了します。) サーバー側からソケットを閉じる主なタイミングは、しばらくの間データがなくなることがわかっている場合です。クライアントに送信する最後のメッセージは、特定の時間に戻ってくるように伝えることです。たとえば、株式ストリーミングの場合、午後 8 時に接続を閉じ、クライアントに 8 時間後に戻ってくるように指示できます (NASDAQ が午前 4 時から午後 8 時までクオートを受け付けていると仮定します)。金曜日の夜、あなたは彼らに月曜日の朝に戻ってくるように言います。(私は SSE に関する本を近々出版する予定であり、このテーマについていくつかのセクションを捧げます。)

「...この場合、PHP はこの種のサーバーに適したテクノロジではありません。しかし、これまでに見たすべてのデモは、PHP がこれに適していることを示唆しています。混乱している..."

PHP は通常の Web サイトに適したテクノロジではないと主張する人がいますが、そのとおりです。LAMP スタック全体を C++ に置き換えれば、はるかに少ないメモリと CPU サイクルでそれを実行できます。しかし、それにもかかわらず、PHP はそこにあるほとんどのサイトをうまく動かしています。これは、使い慣れた C ライクな構文と非常に多くのライブラリの組み合わせにより、Web 作業にとって非常に生産的な言語であり、多くの PHP プログラマーを雇うことができ、多くの本やその他のリソース、およびいくつかの大規模なリソースとして管理者にとって快適な言語です。ユースケース (Facebook や Wikipedia など)。これらは基本的に、ストリーミング テクノロジーとして PHP を選択する理由と同じです。

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

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

于 2013-11-01T00:44:16.050 に答える
4

sse techink が遅延データのすべてのカップルをクライアントに送信することに気付きました (Ajax プーリング データのクライアント ページからプーリング データ techink を逆にするようなものです)。この問題を克服するために、sseServer.php ページでこれを作成しました。

<?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 でセッションを開始し、セッション変数を使用していることに注意してください。問題を克服するために。

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

今、jQuery (javascript) で私はそのようなことをします: 1st) 私はグローバル変数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通知を送信していました。

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

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

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

于 2014-02-13T12:28:09.727 に答える
3

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

これは最も適切な例ではありませんが、適切なビルディング ブロックとして機能するはずです。実際の永続化レイヤーにフックして、これらのイベントのトリガーを処理できます。次に、サーバーに打撃を与えることなく、すぐに (できる限りリアルタイムで) それらを取得します (常にデータベースにクエリを実行して、状況が変化したかどうかを確認する必要がないため)。

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

  • 手動で変更する必要がある問題を修正します
  • プロセスを促進するツールを構築し、これらのイベントを発生させます
于 2013-01-28T15:12:46.847 に答える
-7

基本的に、PHP はこの種の技術には適していません。はい、機能させることはできますが、高負荷では災害になります。私たちは、Websocket を介して何十万ものユーザーに株式変更シグナルを送信する株式サーバーを実行しています。そのために php を使用する場合は... できますが、これらの自家製のサイクルは悪夢にすぎません。単一の接続ごとにサーバー上で個別のプロセスが作成されるか、何らかのデータベースからの接続を処理する必要があります。

nodejs と socket.io を使用するだけです。これにより、数日で簡単にサーバーを起動して実行できます. Nodejs にも独自の制限がありますが、websocket (および SSE) 接続では、最も強力なテクノロジになりました。

また、SSEは見た目ほど良くありません。websockets の唯一の利点は、パケットがネイティブに gzip されている (ws は gzip されていない) ことですが、欠点は SSE が片側接続であることです。ユーザーが別の銘柄記号をサブスクリプションに追加したい場合は、ajax リクエストを行う必要があります (オリジン コントロールに関するすべての問題を含め、リクエストは遅くなります)。Websockets では、クライアントとサーバーは 1 つの開かれた接続で双方向に通信するため、ユーザーが取引シグナルを送信したり、見積もりを購読したりする場合は、既に開かれている接続で文字列を送信するだけです。そしてそれは速いです。

于 2015-04-19T17:47:13.057 に答える