PHPでマルチスレッドを実装する方法はありますか?複数のサーバーに基づいて PHP アプリケーションを作成しているとします。理想的には、リクエストを次々に送信するのではなく、同時に複数のサーバーに送信します。出来ますか?同時実行機能を実装したい場合、通常はフォークまたは生成スレッドの使用を考えますが、PHP がマルチスレッドをサポートしていないことがわかると、考えを変え、Perl などの十分ではない言語を使用する可能性があります。
実際、ほとんどの場合、フォークやスレッドを使用する必要はなく、フォークやスレッドを使用するよりもパフォーマンスが向上します。 n 台の実行中のサーバーが正しく機能していることを確認するサービスを構築するとします。次のようなコードを書くとよいでしょう:
-
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
ログイン後にコピー
ログイン後にコピー
-
$タイムアウト = 15;
$status = array();
-
foreach ($hosts として $host) {
-
$errno = 0;
$errstr = "";
-
$s = fsockopen($host, 80, $errno, $errstr, $timeout);
if ($s)
-
$status[$host] = "接続済み";
fwrite($s, "HEAD / HTTP/1.0rnHost: $hostrnrn");
-
する {
$data = fread($s, 8192);
-
If (strlen($data) == 0) {
-
休憩
-
$status[$host] .= $data;
-
。
-
fclose($s);
-
それ以外 {
$status[$host] = "接続に失敗しました: $errno $errstrn";
-
}-
print_r($ステータス);
-
?>
-
これは問題なく動作しますが、このコードを拡張して多数のサーバーを管理するには、 fsockopen() がホスト名を解析して正常な接続を確立するまで (または $timeout 秒の遅れが生じるまで) 長い時間がかかります。 -
したがって、このコードを放棄する必要があります。fsockopen が接続ステータスを返すのを待つ必要がなく、非同期接続を確立できます。 PHP は依然としてホスト名を解決する必要があります (したがって、IP を直接使用する方が賢明です) が、接続を開いた後すぐにホスト名が返されるため、次のサーバーに接続できます。 -
これを実現するには 2 つの方法があります。PHP5 では、新しい stream_socket_client() 関数を使用して fsocketopen() を直接置き換えることができます。 PHP5 より前のバージョンの場合、問題を解決するには自分で実行し、ソケット拡張機能を使用する必要があります。 PHP5 での解決策は次のとおりです: -
-
$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_CONNECTSTREAM_CLIENT_CONNECT);
-
if ($s) {
-
$sockets[$id] = $s;
-
$status[$id] = 「進行中」;
-
}
-
else { $status[$id] = 「失敗、$errno $errstr」;
-
}
-
}
-
/* さて、結果が 戻ってくるまで待ちます */
-
-
while (count($sockets)) {
-
$read = $write = $ソケット;
-
/* これは 魔法の 機能です - 以下で説明します */
-
$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] == "進行中") {
-
$status[$id] = 「接続に失敗しました」;
-
}
-
fclose($r);
-
unset($sockets[$id]);
-
}
-
他 {
-
$status[$id] .= $data;
-
}
-
}
-
/* 書き込み可能なソケットは HTTP リクエストを受け入れることができます */
-
foreach ($write as $w) {
-
$id = array_search($w, $sockets);
-
fwrite($w, "HEAD / HTTP/1.0rnHost: "
-
。 $hosts[$id] 。 "rnrn");
-
$status[$id] = 「応答を待っています」;
-
}
-
}
-
他 {
-
/* 待機中にタイムアウトになりました。 $sockets に関連付けられたすべてのホストに障害があると仮定します。*/
-
foreach ($sockets as $id => $s) {
-
$status[$id] = 「タイムアウト 」
-
。 $ステータス[$id];
-
}
-
壊す;
-
}
-
}
-
foreach ($hosts as $id => $host) {
-
echo "ホスト: $hostn"; echo 「ステータス: 」
-
。 $status[$id] 。 「ん」;
-
}
-
?>
stream_select() は、システムの select(2) 関数を使用して、ソケットがオープンされるのを待ちます。 上の 3 つのパラメータは、使用されるストリームの数です。
以下は PHP4.1.0 以降のバージョンです。PHP のバージョンにソケット (ext/sockets) が含まれている場合、上にあるストリーム/ファイル システム関数の機能を拡張する必要がある場合のみ、上にあるものと同様のコードを使用できます。 /sockets 関数が実行されます。主な違いは、stream_socket_client() の代わりに次の関数を使用して接続を確立することです:
-
// 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_close()替换掉fclose()就可以执行脚本了! PHP5的先进之处在于,你可以用stream_select()处理几乎所有的stream。例如你可以通过include STDIN用它接收键盘输入并保存进数组,你还可以接收通过proc_open()打开的管道中的数据。