用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("\r\n", $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("\r\n\r\n",$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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM