【问题标题】:Custom Web Proxy: is better to fork process or make http requests?自定义 Web 代理:fork 进程还是发出 http 请求更好?
【发布时间】:2010-11-08 17:10:26
【问题描述】:

对不起这个标题,但很难用几句话来解释。

我写了一个小网络代理——不是 apache 或任何常见的网络服务器——它的作用是执行一些 php 代码。

有两种方法:

1) fork 一个新的 php -f file.php

2) 从网络代理中调用http://localhost/file.php

我认为该代理会有很多并发请求,每个请求将保持活动至少 20-30 秒。

我的问题是:通过 http 分叉和请求哪个更好?

感谢任何提示!

达里奥

【问题讨论】:

  • 添加了一个 LOOOOOONG 示例代码。

标签: php http proxy fork


【解决方案1】:

我最近也做了一个代理。而且 - 还有第三种选择。甚至不需要调用自己或其他脚本,它是完全独立的,并且是跨平台的......

所以 - 首先是我使用套接字完成了这项工作。我想你也这样做了,但只是写在这里以防万一。我将浏览器中的代理设置为特定端口,允许通过防火墙进入侦听 PHP 脚本。为了让它开始监听,我必须在端口 80 上“运行”脚本,所以我还得到了一个不错的实时控制台。

所以 - 脚本侦听套接字,并将其设置为 NON-BLOCKING。它与循环一起工作(无限,需要时使用 break 超时) - *@socket_accept()* 用于查看是否有任何新连接,检查是否 !== false。

最后,循环循环 (:D) 通过所有生成的子套接字,这些子套接字存储在一个数组中。它是一个 HTTP 代理,所以它们都有一些特定的阶段(客户端发送标头,尝试到达远程服务器,发送客户端标头,从远程服务器接收数据)。诸如读取(在使用 fsockopen() 打开的远程套接字上)通常会被阻塞,并且无法将其设置为非阻塞的事情,是通过“模拟”非阻塞模式一个非常极端的超时——比如 5 微秒,最长。读取的字符数为 128。

tl;博士;本质上,这就像处理器多线程。

但是!!!如果这样做,您需要套接字。

编辑:添加一个从所有自定义操作中剥离的示例代码:(是的,它很长)

set_time_limit(0); // so we don't get a timeout from PHP
// - can lead to sockets left open...

$config_port = 85; // port
$config_address = "192.168.0.199"; // IP address, most likely local with router
// if there's not a router between server and wan, use the wan IP
$config_to = 30; // global timeout in seconds
$config_connect_to = 2; // timeout for connecting to remote server
$config_client_to = 0.1; // timeout for reading client data
$config_remote_to = 10; // timeout for reading remote data (microseconds)
$config_remote_stage_to = 15; // timeout for last stage (seconds)
$config_backlog = 5000; // max backlogs, the more the better (usually)

$parent_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // parent socket
$tmp_res = @socket_bind($parent_sock, $config_address, $config_port);
if ($tmp_res === false){
    echo "Can't bind socket.";
    exit;
    // address or port in use, for example by Apache
}

$tmp_res = @socket_listen($parent_sock, $config_backlog);
if ($tmp_res === false){
    echo "Can't start socket listener.";
    exit;
    // hard to tell what can cause this
}

socket_set_nonblock($parent_sock); // non-blocking mode

$sockets = array(); // children sockets
$la = time(); // last activity
while (time() - $la < $config_to){
    $spawn = @socket_accept($parent_sock); // check for new connection
    if ($spawn !== false){
        $la = time();

        $ni = count($sockets);
        $sockets[$ni] = array();
        $sockets[$ni]["handle"] = $spawn;
        $sockets[$ni]["stage"] = 1;
        $sockets[$ni]["la"] = time(); // for some stages
        $sockets[$ni]["client_data"] = "";
        $sockets[$ni]["headers"] = array();
        $sockets[$ni]["remote"] = false;
    }

    foreach ($sockets as &$sock){ // &$sock because we're gonna edit the var
        switch ($sock["stage"]){
        case 1: // receive client data
        $read_data = @socket_read($sock["handle"], 2048);
        if ($read_data !== false && $read_data !== ""){
            $la = time();
            $sock["la"] = microtime(true);
            $sock["client_data"] .= $read_data;
        } else if (microtime(true) - $sock["la"] > $config_client_to) {
            // client data received (or too slow :D)
            $sock["stage"] = 2; // go on
        }
        break;
        case 2: // connect to remote
        $headers = explode("\r\n", $sock["client_data"]);
        foreach ($headers as $hdr){
            $h_pos = strpos($hdr, ":");
            if ($h_pos !== false){
                $nhid = count($sock["headers"]);
                $sock["headers"][strtolower(substr($hdr, 0, $h_pos))] = ltrim(substr($hdr, $h_pos + 1));
            }
        }

        // we'll have to use the "Host" header to know target server
        $sock["remote"] = @fsockopen($sock["headers"]["host"], 80, $sock["errno"], $sock["errstr"], $config_connect_to);
        if ($sock["remote"] === false){
            // error, echo it and close client socket, set stage to 0
            echo "Socket error: #".$sock["errno"].": ".$sock["errstr"]."<br />\n";
            flush(); // immediately show the error
            @socket_close($sock["handle"]);
            $sock["handle"] = 0;
        } else {
            $la = time();
            // okay - connected
            $sock["stage"] = 3;
        }
        break;
        case 3: // send client data
        $tmp_res = @fwrite($sock["remote"], $sock["client_data"]);
        // this currently supports just client data up to 8192 bytes long
        if ($tmp_res === false){
            // error
            echo "Couldn't send client data to remote server!<br />\n";
            flush();
            @socket_close($sock["handle"]);
            @fclose($sock["remote"]);
            $sock["stage"] = 0;
        } else {
            // client data sent
            $la = time();
            stream_set_timeout($sock["remote"], $config_remote_to);
            $sock["la"] = time(); // we'll need this in stage 4
            $sock["stage"] = 4;
        }
        break;
        case 4:
        $remote_read = @fread($sock["remote"], 128);
        if ($remote_read !== false && $remote_read !== ""){
            $la = time();
            $sock["la"] = time();
            @socket_write($sock["handle"], $remote_read);
        } else {
            if (time() - $sock["la"] >= $config_remote_stage_to){
                echo "Timeout.<br />\n";
                flush();
                @socket_close($sock["handle"]);
                @fclose($sock["remote"]);
                $sock["stage"] = 0;
            }
        }
        break;
        }
    }
}

foreach($sockets as $sock){
    @socket_close($sock["handle"]);
    @fclose($sock["remote"]);
}
@socket_close($parent_sock);

【讨论】:

  • 这不正是我的想法,但你建议我在 apache 和分叉上的一个很好的选择:编写另一个网络服务器,其角色将是执行该 file.php。谢谢!
  • 有一个非常棘手的问题:如果代理到远程主机连接持续一段时间,这将阻塞整个服务器.. 有必要拥有一个真正的多线程服务器。有什么建议吗?
  • 这是一个多线程服务器 - 在 PHP 中,而不是进程。它并没有真正阻止它——如您所见,远程主机的每个读取操作都有 10 微秒的超时。这非常快。此外,即使网络连接非常快,最大。读取 128 个字符以使整个过程更快。不过,有些部分可以做得更好,例如写入远程服务器 - 它目前没有“线程化”。
猜你喜欢
  • 2017-09-02
  • 2012-04-30
  • 2013-05-24
  • 2017-02-11
  • 2013-05-31
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多