什么是反向代理:
百度百科有雲:
反向代理(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