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


91

すべて、

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あなたが同時に実行しているユーザーと同じ数のスレッドを実行していることを意味しますか?もしそうなら-それはリモートで実現可能ですか、それともアプリケーションを構築する適切な方法ですか?あなたがすることができたときに、どのように知っていますENDのインスタンスをstream.php

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


これは、開発者が自分でコーディングする必要がある部分です。データを取得する手段は、WebSockets /ロングポーリングなどです。ただし、トリックはイベントをトリガーするものです。個人的に、私はいくつかのアプローチを試しましたが、私が気に入った1つのアプローチ(ただしフェールセーフではありませんでし)は、特定のテーブルに何かが挿入されるたびにMySQLにコンソールプログラムをトリガーさせることでした。コンソールプログラムは、変更または挿入されたレコードに関する情報を受け取り、WebSocketを介して対応するユーザーに通知を送信します。基本的に、私はPHPデーモンにメッセージの送信を待機させていました。
NB 2013年

これで一つの問題は、SSEは、IEでサポートされていません: - /また、私は、この読んでいましたprodigyproductionsllc.com/articles/programming/javascript/...を私は彼があまりにも多く、子どもたちの問題ではなくのような全体的なルックスを避けるためにポートを使用していると思う彼のSSEを回避することをお勧めします。IMO、それよりもはるかに厄介なように見えます。
PJ Brunet 2013年

現在IE11またはAndroidブラウザーでサポートされていませんcaniuse.com/eventsource
PJ Brunet

1

4
私は、同じ質問を持っていたし、私は深くあなたが何を意味するかを理解すると思う...サーバー上のイベントをトリガするもの。のオブジェクトを作成するEventSource('stream.php')と、クライアントstream.phpはajaxから呼び出すような接続を開きます。この接続はサーバー側のコードをトリガーし、サーバー側のコードが何か言う間は接続を開いたままにします。次に、接続が閉じ、少し遅れて(Chromeで3秒だと思います)、クライアントが接続を再び開き、stream.phpファイルが再びトリガーされます。
Ahmad Maleki 2017

回答:


28

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

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

「そして、stream.phpのインスタンスをいつ終了できるか、どうやってわかりますか?」

最も一般的なケースでは、これはユーザーがサイトを離れたときに発生します。(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への単一の接続、またはクラスター内の各マシンからNASDAQへの単一の接続を使用する別のプロセスを使用します。次に、価格をSQL / NoSQLサーバーまたは共有メモリにプッシュします。次に、PHPはその共有メモリ(またはデータベース)をポーリングし、データをプッシュします。または、データ収集サーバーがあり、各PHPインスタンスがそのサーバーへのソケット接続を開きます。データ収集サーバーは、更新を受信すると、各PHPクライアントに更新をプッシュし、次に、そのデータをクライアントにプッシュします。

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


各Apache接続プロセスはメモリを消費するので、代わりにNginxを使用する方が良いでしょうか?
Zhang Buzz

@ZhangBuzz私がApache + PHPを言ったところで、それは本当に「ウェブサーバー+ PHPプロセス」を意味するので、基本的に別のウェブサーバーを使用することとの違いはありません。
ダレン・クック

たぶんこれらのようなサーバー?github.com/hoaproject/Eventsourceまたはgithub.com/hhxsv5/php-sse
Enrique

また、メモリ使用量が少ないため、Nginxの方がはるかに効率的であることに注意してください。blog.webfaction.com
Enrique

31

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

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

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

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


5
「スクリプトの実行時間を制御することで解決でき、時間を超えたときにスクリプトを終了する」について詳しく説明していただけませんか?ユーザーの数が多すぎる場合、ユーザーは3秒後に再び接続するだけなので、接続を閉じることでリソースの使用量が大幅に改善されますか?
ルーク

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)で私はそのようなことをします: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通知を送信するために使用しました。

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

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

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


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);

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

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

  • 手動で変更する必要がある問題を修正する
  • プロセスを迅速化するツールを構築し、これらのイベントを発生させる

3
コリン-答えてくれてありがとう。私のせい-私の質問ははっきりしていません-しかし、これは私が求めていることではありません。私は何を意味依頼することは、これはある...あなたはあなたの「サーバー」としてPHPを使用している場合-ん、あなたが実行するために、あなたのクライアントのニーズにのEventSourceから呼び出すPHPスクリプト全体時間クライアントがそれに接続されているの?つまり、1,000の同時ユーザーがいる場合、1,000の個別のスレッドがPHPスクリプトの1,000の同時インスタンスを実行しているということですか。それは実現可能ですか?そして、phpスクリプトをいつ終了するかを知っていますか(「生きている」ためにループしていると仮定します)。
mattstuehler 2013年

-7

基本的に、PHPはこの種のことには適した技術ではありません。はい、あなたはそれを働かせることができます、しかしそれは高負荷の災いです。私たちは、Webソケットを介して数十人のThousendsユーザーに在庫変更信号を送信するストックサーバーを実行しています。そのためにphpを使用する場合...できますが、これらの自家製サイクルは悪夢です。接続ごとにサーバー上で個別のプロセスが作成されるか、何らかのデータベースからの接続を処理する必要があります。

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

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


2
React.phpは、基本的にはnode.jsのイベントループと同じ方法で使用できます。
マテイKoubík

3
PHPが最良の選択ではないことを伝えるのは良いことですが、少なくともOPが要求するものを含める必要があると思います。
Nishchal Gautam 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.