• 用PHP实现反向代理服务器


    什么是反向代理:

    百度百科有云:

    反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

    反向代理目的也各有不同,有用作CDN的,有作为负载均衡的等等。

    成熟的反向代理的软件有很多:我比较熟悉nginx,性能高,功能强大,配置简单,作为负载均衡的工具绝对是杠杠的。

    作为一个程序员,以上都是废话,下面进入正题。

    为了快速的完成功能(其实就是懒),我首先做的就是google,看看是否有php实现的反向打理程序。事实上还真找到了。有7ghost、phpproxy等等,可惜他们要不是用fsockopen写的。我不是很看得懂,看不懂,就很难修改和扩展,更好的为自己的需求工作,要不就是功能上好像有些不符合我们的一些测试。于是一咬牙就写了php基于curl的反向代理脚本。

    php是一个脚本语言,也就是说它的执行效率肯定是不如C,也不如JS,而且传统的php也无法利用事件驱动IO,所以在性能上无法和nginx、nodejs实现的程序相比,如果条件允许,优先使用更好的实现工具。

    但是不得以只能使用的情况下,提高性能就是必须的了。而提高性能的秘密就是少做事,只做一件事,那就是做好请求数据的搬运工,保留HTTP的美好的特性,比如:浏览器缓存,gzip压缩,但是php不做额外的操作,比如:负载均衡,根据缓存头缓存内容等。

    实现的逻辑主要就是一下三步:

    1. 从$_SERVER获取浏览器请求的内容,传说中的Request,并进行一些修改。

    2. 用curl将Request发到后端机器上,并等待后端的返回内容 传说中的Response。

    3. Response中包含Header和Body,分别用header函数和echo函数将它们发到浏览器渲染。

    4. 用rewrite规则将请求发给index.php上执行,这个很容易,代码就不贴了。

    代码地址如下:

    https://gitee.com/jamesren_781/codes/e9v45qa68wndrzbh3ktof

    题外话:我曾经看到我的同事写过一个类似反向代理的实现功能,使用的方法很简单:

    echo file_get_contents($url);

    后来发现如果url是一个图片的话不行,因为content-type不对,浏览器无法识别。结果写了一套根据url后缀识别content-type的方法,等于是实现了一套服务器规则,使得浏览器显示正常了,当时他还得意了很久。这种实现丢失了很多好的东西,比如缓存,gzip等等,还白白浪费了性能。

    所以,人生就是奇妙,有时候一些简单的代码和逻辑反而包含着更高的智慧。而复杂的实现反而在各方面都不如它。有时候我在想为什么别人赚钱这么容易,我这么辛苦还赚得少?也许这就是智慧的高低,这就是道,就是极限挑战里常说的:这就是命。

    <?php 
    set_time_limit(60);
    if( !defined('__DIR__') )
    {
      define('__DIR__',dirname(__FILE__)) ;
    }
    
    $_REQUEST['url'] =gtRootUrl();
    //改成网站正式服务器ip
    $ip= '127.0.0.1';
    $aAccess = curl_init() ;
    // --------------------
    // set URL and other appropriate options
    curl_setopt($aAccess, CURLOPT_URL, $_REQUEST['url']);
    curl_setopt($aAccess, CURLOPT_HEADER, true);
    curl_setopt($aAccess, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($aAccess, CURLOPT_FOLLOWLOCATION, false);
    curl_setopt($aAccess, CURLOPT_SSL_VERIFYPEER, false);  
    curl_setopt($aAccess, CURLOPT_SSL_VERIFYHOST, false);  
    curl_setopt($aAccess, CURLOPT_TIMEOUT, 60);
    curl_setopt($aAccess, CURLOPT_BINARYTRANSFER, true);
    //curl_setopt($aAccess, CURLOPT_HTTPPROXYTUNNEL, 0);
    curl_setopt($aAccess,CURLOPT_PROXY,$ip.':80');
    //curl_setopt($aAccess,CURLOPT_PROXY,'127.0.0.1:8888');
    
    if(!empty($_SERVER['HTTP_REFERER']))
        curl_setopt($aAccess,CURLOPT_REFERER,$_SERVER['HTTP_REFERER']) ;
    
    
    
    $headers=get_client_header();
    curl_setopt($aAccess,CURLOPT_HTTPHEADER,$headers) ;
    
    if( $_SERVER['REQUEST_METHOD']=='POST' )
    {
        curl_setopt($aAccess, CURLOPT_POST, 1);
        curl_setopt($aAccess, CURLOPT_POSTFIELDS, http_build_query($_POST));
    }
    
    
    // grab URL and pass it to the browser
    
    $sResponse = curl_exec($aAccess);
    list($headerstr,$sResponse)=parseHeader($sResponse);
    $headarr= explode("
    ", $headerstr);
    foreach($headarr as $h){
        if(strlen($h)>0){
            if(strpos($h,'Content-Length')!==false) continue;
            if(strpos($h,'Transfer-Encoding')!==false) continue;
            if(strpos($h,'Connection')!==false) continue;
            if(strpos($h,'HTTP/1.1 100 Continue')!==false) continue;
            header($h);
        }
    }
    
    function replace_html_path($arrMatche)
    {    
        $sPath = makeUrl($arrMatche[4]) ;
        if( strtolower($arrMatche[1])=='img' )
        {
            $sPath.= '&bin=1' ;
        }
        
        return "<{$arrMatche[1]} {$arrMatche[2]} {$arrMatche[3]}="{$sPath}"" ;
    }
    
    function get_client_header(){
        $headers=array();
        foreach($_SERVER as $k=>$v){
            if(strpos($k,'HTTP_')===0){
                $k=strtolower(preg_replace('/^HTTP/', '', $k));
                $k=preg_replace_callback('/_w/','header_callback',$k);
                $k=preg_replace('/^_/','',$k);
                $k=str_replace('_','-',$k);
                if($k=='Host') continue;
                $headers[]="$k:$v";
            }
        }
        return $headers;
    }
    
    function header_callback($str){
        return strtoupper($str[0]);
    }
    
    function parseHeader($sResponse){
        list($headerstr,$sResponse)=explode("
    
    ",$sResponse, 2);
        $ret=array($headerstr,$sResponse);
        if(preg_match('/^HTTP/1.1 d{3}/', $sResponse)){
            $ret=parseHeader($sResponse);
        }
        return $ret;
    }
    
    function gtRootUrl()
    {
    //缓存结果,同一个request不重复计算
     static $gtrooturl;
     if(empty($gtrooturl)){
        // Protocol
        $s = !isset($_SERVER['HTTPS']) ? '' : ($_SERVER['HTTPS'] == 'on') ? 's' : '';
        $protocol = strtolower($_SERVER['SERVER_PROTOCOL']);
        $protocol = substr($protocol,0,strpos($protocol,'/')).$s.'://';
        // Port
        $port = ($_SERVER['SERVER_PORT'] == 80) ? '' : ':'.$_SERVER['SERVER_PORT'];
        // Server name
        $server_name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'].$port : getenv('SERVER_NAME').$port;
        // Host
        $host = isset($_SERVER['HTTP_HOST']) ? strtolower($_SERVER['HTTP_HOST']) : $server_name;
    
         $gtrooturl=$protocol.$host.$_SERVER['REQUEST_URI'];
        }
            return $gtrooturl;
    }
    
    // close cURL resource, and free up system resources
    curl_close($aAccess);
    echo $sResponse ;

    转自:https://my.oschina.net/jamesren/blog/668495  https://www.cnblogs.com/jasonxu19900827/p/7810006.html

  • 相关阅读:
    HDU 1016 Prime Ring Problem
    CreateRemoteThread函数多參数传入用法
    Dynamics CRM2015 on-premises直接升级Dynamics CRM2016 on-premises
    cocos2d-x+lua代码热载入(Hot Swap)的研究
    DirectX 9.0c游戏开发手记之“龙书”第二版学习笔记之8: Chap10: Lighting
    js合并table单元格(拼table的时候并不知道详细几行几列)
    简单图模板 Graph
    POJ-3278-Catch That Cow-广搜(BFS)
    android用存到缓存的方法来保存ListView里的数据
    Ubuntu 14.10中连接Win10的smb共享文件
  • 原文地址:https://www.cnblogs.com/7qin/p/10651123.html
Copyright © 2020-2023  润新知