PHP 單線程模型,但可通過異步 IO 或進程分離實現並發效果。
$hosts = array("host1", "host2", "host3");
$timeout = 15;
$sockets = array();
// 並發發起連接
foreach ($hosts as $id => $host) {
$s = stream_socket_client("tcp://$host:80", $errno, $errstr, $timeout,
STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT);
if ($s) { $sockets[$id] = $s; $status[$id] = "in progress"; }
}
// 使用 stream_select 監聽就緒流
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) { fclose($r); unset($sockets[$id]); }
else { $status[$id] .= $data; }
}
foreach ($write as $w) {
$id = array_search($w, $sockets);
fwrite($w, "HEAD / HTTP/1.0\r\nHost: {$hosts[$id]}\r\n\r\n");
$status[$id] = "waiting response";
}
}
}
class Thread {
var $hooks=array(), $args=array();
function addThread($func) {
$this->hooks[] = $func;
$this->args[] = array_slice(func_get_args(), 1);
}
function runThread() {
if (isset($_GET['flag'])) {
call_user_func_array($this->hooks[$_GET['flag']], $this->args[$_GET['flag']]);
} else {
foreach ($this->hooks as $i=>$func) {
$fp = fsockopen($_SERVER['HTTP_HOST'], $_SERVER['SERVER_PORT']);
fputs($fp, "GET {$_SERVER['PHP_SELF']}?flag=$i HTTP/1.1\r\nHost: {$_SERVER['HTTP_HOST']}\r\nConnection: Close\r\n\r\n");
fclose($fp); // 非阻塞發起子請求
}
}
}
}
// 使用:
$t = new Thread();
$t->addThread('func1', $arg1);
$t->addThread('func2', $arg2);
$t->runThread();
| 方案 | 優點 | 缺點 |
|---|---|---|
| stream_socket_client | 真異步IO、單進程高併發、PHP5.3+原生 | 代碼較複雜、需理解流選擇機制 |
| fsockopen分離請求 | 思路簡單、兼容舊版本、易理解 | 實際開啟多進程、資源消耗大、受服務器並發限制 |