php fsockopenにおけるマルチスレッド問題の解決方法[翻訳]
問題:phpでマルチスレッドを実現する方法がありますか?複数のサーバに基づくphpアプリケーションを書いていると仮定します。理想的な場合は、複数のサーバに対して、一台ではなく、同時に要求を送信します。叶えられますか回答:合併機能を実現するためには、forkやspawn threadsを使いたいという人がいますが、彼らはphpがマルチスレッドをサポートしていないことを発見した時、たぶんいくつかの足りない言語、たとえばperlを使って考えを変えます。実際にはほとんどの場合、forkやスレッドを使う必要はなく、forkやthreadを使うよりも優れた性能を得ることができます。サービスを確立して、実行中のn台サーバを確認して、彼らが正常に動作していることを確認します。あなたは下記のようなコードを書くかもしれません。
<?php
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
$timeout = 15; $status = array();
foreach ($hosts as $host) {
$errno = 0;
$errstr = "";
$s = fsockopen($host, 80, $errno, $errstr, $timeout);
if ($s) {
$status[$host] = "Connectedn";
fwrite($s, "HEAD / HTTP/1.0rnHost: $hostrnrn");
do {
$data = fread($s, 8192);
if (strlen($data) == 0) { break; }
$status[$host] .= $data;
} while (true); fclose($s);
} else {
$status[$host] = "Connection failed: $errno $errstrn";
}
}
print_r($status);
?>
はよく実行されていますが、fsockopen()はホームnameを分析して成功した接続を作る前に、このコードを拡張して大量のサーバーを管理するのに時間がかかります。このコードを放棄しなければなりません。非同期接続を確立できます。fsockopenが接続状態に戻るまで待つ必要はありません。PHPはまだ解析が必要ですが、接続を開けたらすぐに戻ります。次のサーバーに接続できます。二つの方法が実現できる。PHP 5では、追加のstream_を使用できます。socketclient関数を直接に置換します。fsocketopen()。PHP 5以前のバージョンは、自分で作業し、socketsで拡張して問題を解決する必要があります。以下はPHP 5の解決方法です。
<?php
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
$timeout = 15;
$status = array();
$sockets = array();
foreach ($hosts as $id => $host) {
$s = stream_socket_client("$host:80", $errno, $errstr, $timeout,STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
if ($s) {
$sockets[$id] = $s;
$status[$id] = "in progress";
} else {
$status[$id] = "failed, $errno $errstr";
}
}
while (count($sockets)) {
$read = $write = $sockets;
$n = stream_select($read, $write, $e = null, $timeout);
if ($n > 0) {
foreach ($read as $r) {
$id = array_search($r, $sockets);
$data = fread($r, 8192);
if (strlen($data) == 0) {
if ($status[$id] == "in progress") {
$status[$id] = "failed to connect";
}
fclose($r);
unset($sockets[$id]);
} else {
$status[$id] .= $data;
}
}
foreach ($write as $w) {
$id = array_search($w, $sockets);
fwrite($w, "HEAD / HTTP/1.0rnHost: " . $hosts[$id] . "rnrn");
$status[$id] = "waiting for response";
}
} else {
foreach ($sockets as $id => $s) {
$status[$id] = "timed out " . $status[$id];
}
break;
}
}
foreach ($hosts as $id => $host) {
echo "Host: $hostn"; echo "Status: " . $status[$id] . "nn";
}
?>
私達はsteam_を使います。select()socketsが開いている接続イベントを待つ。ストリームselect()システムのselect(2)関数を呼び出して作業します。前の三つのパラメータはあなたが使うべきstremsの配列です。これを読み込み、書き込み、取得することができます。ストリームselect()は、イベントの発生~イベントの発生を待っている間に、timeout(秒)パラメータを設定することにより、該当するsocketsデータをあなたの着信パラメータに書き込みます。以下はPHP 4.1.0以降のバージョンの実装です。PHPをコンパイルする時にsockets(ext/sockets)のサポートが含まれている場合、上のようなコードを使ってもいいです。上のstrems/filesystem関数の機能をext/sockets関数で実現するだけです。主な違いは私達がstreamを下の関数で代用することです。socketclient()は接続を確立します。
<?php
// This value is correct for Linux, other systems have other values
define('EINPROGRESS', 115);
function non_blocking_connect($host, $port, &$errno, &$errstr, $timeout) {
$ip = gethostbyname($host);
$s = socket_create(AF_INET, SOCK_STREAM, 0);
if (socket_set_nonblock($s)) {
$r = @socket_connect($s, $ip, $port);
if ($r || socket_last_error() == EINPROGRESS) {
$errno = EINPROGRESS; return $s;
}
}
$errno = socket_last_error($s);
$errstr = socket_strerror($errno);
socket_close($s);
return false;
}
?>
は今socketを使います。select()を取り替えるとstream_select()は、socketを使います。read()をfread()に差し替え、socket_を使う。write()を置換してfwrite()を落として、socket_を使います。fcloseを置き換えるとスクリプトが実行されます。PHP 5の先進的なところは、streamを使ってもいいです。select()はほとんどのstreamを処理します。例えば、include STDINを通じてキーボード入力を受信して配列に保存することができます。プロクを通しても受信できます。オープンしたパイプの中のデータ。PHP 4.3.x自身にstremsを処理する機能を持たせたいなら、fsockopenが非同期で作動できるようにするためのpatchを用意しました。このパッチの使用には賛成していません。このパッチは公式のPHPバージョンには現れません。パッチにはstream_が添付されています。socketCient()関数の実装により、PHP 5にスクリプトを互換させることができます。