8

これが私の問題です。AJAXクライアントスクリプトによって要求されるスクリプト(comet.phpと呼びましょう)があり、次のような変更が行われるのを待ちます。

while(no_changes){
    usleep(100000);
    //check for changes
}

私はこれがあまり好きではありません、それはあまりスケーラブルではなく、それは(私見)「悪い習慣」です私はセマフォ(?)またはとにかく並行プログラミング技術でこの振る舞いを改善したいと思います。これを処理するためのヒントを教えてください。(私は知っています、それは短い答えではありませんが、出発点で十分でしょう。)

編集LibEventはどうですか?

4

5 に答える 5

14

ZeroMQを使用してこの問題を解決できます。

ZeroMQは、物事(スレッド、プロセス、さらには個別のマシン)を一緒に接続するための過給されたソケットを提供するライブラリです。

サーバーからクライアントにデータをプッシュしようとしていると思います。そのための良い方法は、EventSource APIポリフィルが利用可能)を使用することです。

client.js

EventSourceを介してstream.phpに接続します。

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

stream.addEventListener('debug', function (event) {
    var data = JSON.parse(event.data);
    console.log([event.type, data]);
});

stream.addEventListener('message', function (event) {
    var data = JSON.parse(event.data);
    console.log([event.type, data]);
});

router.php

これは、着信メッセージをリッスンし、リッスンしているすべての人に送信する、長時間実行されるプロセスです。

<?php

$context = new ZMQContext();

$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind("tcp://*:5555");

$pub = $context->getSocket(ZMQ::SOCKET_PUB);
$pub->bind("tcp://*:5556");

while (true) {
    $msg = $pull->recv();
    echo "publishing received message $msg\n";
    $pub->send($msg);
}

stream.php

サイトに接続するすべてのユーザーは、独自のstream.phpを取得します。このスクリプトは長時間実行され、ルーターからのメッセージを待ちます。新しいメッセージを取得すると、このメッセージをEventSource形式で出力します。

<?php

$context = new ZMQContext();

$sock = $context->getSocket(ZMQ::SOCKET_SUB);
$sock->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "");
$sock->connect("tcp://127.0.0.1:5556");

set_time_limit(0);
ini_set('memory_limit', '512M');

header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");

while (true) {
    $msg = $sock->recv();
    $event = json_decode($msg, true);
    if (isset($event['type'])) {
        echo "event: {$event['type']}\n";
    }
    $data = json_encode($event['data']);
    echo "data: $data\n\n";
    ob_flush();
    flush();
}

すべてのユーザーにメッセージを送信するには、メッセージをルーターに送信するだけです。その後、ルーターはそのメッセージをすべてのリスニングストリームに配信します。次に例を示します。

<?php

$context = new ZMQContext();

$sock = $context->getSocket(ZMQ::SOCKET_PUSH);
$sock->connect("tcp://127.0.0.1:5555");

$msg = json_encode(array('type' => 'debug', 'data' => array('foo', 'bar', 'baz')));
$sock->send($msg);

$msg = json_encode(array('data' => array('foo', 'bar', 'baz')));
$sock->send($msg);

これは、リアルタイムプログラミングを行うためにnode.jsが必要ないことを証明するはずです。PHPはそれをうまく処理できます。

それとは別に、socket.ioはこれを行うための本当に良い方法です。また、ZeroMQを介してsocket.ioからPHPコードに簡単に接続できます。

も参照してください

于 2011-12-28T20:30:21.493 に答える
4

それは本当にあなたがあなたのサーバーサイドスクリプトで何をしているかに依存します。上記のことをしなければならない状況がいくつかあります。

ただし、何かが発生するまでブロックする関数の呼び出しを伴う何かをしている場合は、usleep()呼び出しの代わりにこれを使用してレースを回避できます(これは「悪い習慣」と見なされる部分です)。

ブロックするファイルまたは他の種類のストリームからのデータを待っていたとします。あなたはこれを行うことができます:

while (($str = fgets($fp)) === FALSE) continue;
// Handle the event here

本当に、PHPはこのようなことをするのに間違った言語です。しかし、PHPが唯一の選択肢である状況があります(私はそれらを自分で扱ったことがあるので知っています)。

于 2011-09-19T10:28:42.647 に答える
2

私はPHPが好きですが、PHPはこのタスクに最適な選択ではないと言わざるを得ません。Node.jsは、この種のものにははるかに優れており、非常に優れたスケーリングを実現します。JSの知識があれば、実装も非常に簡単です。

ここで、CPUサイクルを無駄にしたくない場合は、特定のポートである種のサーバーに接続するPHPスクリプトを作成する必要があります。指定されたサーバーは、選択されたポートで接続をリッスンし、X時間ごとにチェックしたいもの(たとえば、新しい投稿のdbエントリ)をチェックしてから、接続されたすべてのクライアントに新しいエントリの準備ができたことを示すメッセージを送信します。

現在、このイベントキューアーキテクチャをPHPで実装することはそれほど難しくありませんが、Node.jsとSocket.IOを使用してこれを行うには、ほとんどのブラウザーで機能するかどうかを心配することなく、文字通り5分かかります。

于 2011-09-19T10:49:24.303 に答える
0

PHPはここでの最善の解決策ではないというここでのコンセンサスに同意します。サーバーからクライアントにデータを配信するというこの非同期の問題を解決するために、専用のリアルタイムテクノロジーを検討する必要があります。クロスブラウザを解決するのは簡単ではないHTTP-LongPollingを実装しようとしているようです。これはComet製品の開発者によって何度も取り組まれてきたので、Cometソリューション、または古いブラウザーのフォールバックをサポートするWebSocketソリューションを検討することをお勧めします。

PHPに得意なWebアプリケーション機能を実行させ、リアルタイムのイベント付き非同期機能専用のソリューションを選択することをお勧めします。

于 2011-09-20T09:54:21.677 に答える
0

リアルタイムライブラリが必要です。

一例はラチェットhttp://socketo.me/です

pub subを処理する部分については、http://socketo.me/docs/wampで説明されています。

ここでの制限は、PHPも可変データを開始するためのものである必要があるということです。

言い換えれば、これはMySQLが更新されたときに魔法のようにサブスクライブすることを可能にしません。ただし、MySQL設定コードを編集できる場合は、そこに公開部分を追加できます。

于 2016-04-19T01:14:26.990 に答える