巧妙解决 PHP 无法实现多线程的问题
有没有办法在 PHP 中实现多线程呢?假设你正在写一个基于多台服务器的 PHP 应用,理想的情况时同时向 多台服务器发送请求,而不是一台接一台。可以实现吗?当有人想要实现并发功能时,他们通常会想到用 fork 或者 spawn threads,但是当他们发现 PHP 不支持多线程的时候,大概会转换思路去用一些不够好的 语言,比如 Perl。
其实的是大多数情况下,你大可不必使用 fork 或者线程,并且你会得到比用 fork 或 thread 更好的性能。 假设你要建立一个服务来检查正在运行的 n 台服务器,以确定他们还在正常运转。你可能会写下面这样的 代码: 1. $hosts = array("host1.sample", "host2.sample", "host3.sample.c om"); 2. $timeout = 15; 3. $status = array(); 4. foreach ($hosts as $host) { 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. } 25. print_r($status); 26. ?> } } else { $status[$host] = "Connection failed: $errno $errstrn"; } while (true); fclose($s); $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;
它运行的很好,但是在 fsockopen()分析完 hostname 并且建立一个成功的连接(或者延时$timeout 秒)之 前,扩充这段代码来管理大量服务器将耗费很长时间。
因此我们必须放弃这段代码;我们可以建立异步连接-不需要等待 fsockopen 返回连接状态。PHP 仍然需要 解析 hostname(所以直接使用 ip 更加明智),不过将在打开一个连接之后立刻返回,继而我们就可以连 接下一台服务器。
有两种方法可以实现;
PHP5 中可以使用新增的 stream_socket_client()函数直接替换掉 fsocketopen()。 PHP5 之前的版本,你需要自己动手,用 sockets 扩展解决
问题。下面是 PHP5 中的解决方法: 1. $hosts = array("host1.sample", "host2.sample", "host3.sample.c om"); 2. $timeout = 15; 3. $status = array(); 4. $sockets = array(); 5. /* Initiate connections to all the hosts simultaneously */ 6. foreach ($hosts as $id => $host) { 7. t, 8. 9. 10. 11. 12. 13. 14. 15. } 16. /* Now, wait for the results to come back in */ 17. 18. while (count($sockets)) { 19. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. } fclose($r); unset($sockets[$id]); } $read = $write = $sockets; $n = stream_select($read, $write, $e = null, $timeout); if ($n > 0) { /* readable sockets either have data for us, or are failed onnection attempts */ 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"; * c 20. /* This is the magic function - explained below */ } else { }
$status[$id] = "failed, $errno $errstr"; STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT); if ($s) { $sockets[$id] = $s; $status[$id] = "in progress"; $s = stream_socket_client("$host:80", $errno, $errstr, $timeou
34. 35. 36. 37. }
else { $status[$id] .= $data; }
38. /* writeable sockets can accept an HTTP request */ 39. foreach ($write as $w) { 40. 41. 42. 43. 44. 45. } 46. else { 47. /* timed out waiting; assume that all hosts associated ts are faulty */ 48. foreach ($sockets as $id => $s) { 49. 50. 51. 52. break; 53. 54. } 55. foreach ($hosts as $id => $host) { 56. 57. 58. } 59. ?> echo "Host: $hostn"; echo "Status: " . $status[$id] . "nn"; } $status[$id] = "timed out " . $status[$id]; } * with $socke $id = array_search($w, $sockets); fwrite($w, "HEAD / HTTP/1.0rnHost: " . $ho