什么是反向代理:

百度百科有云:

反向代理(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

用PHP实现反向代理服务器的更多相关文章

  1. 【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器

    一.反向代理:Web服务器的“经纪人” 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从 ...

  2. Nginx搭建反向代理服务器过程详解

    一.反向代理:Web服务器的“经纪人” 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从 ...

  3. Windows下配置Squid反向代理服务器

    Squid是一款类Unix系统下非常流行的服务器软件,其最重要的功能就是在客户端和服务端之间建立缓存.因而Squid可以用作反向代理,部署多级缓存或者搭建CDN等,无论名称是什么,本质上都是一样的.目 ...

  4. apache 配置反向代理服务器

    前几天实验了下 nginx 配置反向代理服务器.虽然 nginx 的安装很麻烦,但是用起来倒是很简单.不过组里没人用过 nginx,apache 服务器大家倒是蛮熟,为了减少 ops team 的工作 ...

  5. 【翻译】使用nginx作为反向代理服务器,uWSGI作为应用服务器来部署flask应用

    最近在看关于Docker和Nginx方面的内容,先于在Docker上开发以及部署python应用自然要先能够在本机上部署,其中找到一篇文章写的最为详细并且实验成功,所以在此翻译转载过来以备后需.[原文 ...

  6. Nginx搭建反向代理服务器

    [大型网站技术实践]初级篇:借助Nginx搭建反向代理服务器   一.反向代理:Web服务器的“经纪人” 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受int ...

  7. Nginx 搭建反向代理服务器过程详解

    1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet ...

  8. 简单聊聊不可或缺的Nginx反向代理服务器--实现负载均衡【上篇】

    今天又是新的一周,我养足了精神去对待新一周的工作,但是今天到公司发现还是有一点空闲时间的,所以就想与之前接触过的Nginx再交往得更深一点儿. 什么是Nginx: Nginx是一款高性能的http服务 ...

  9. 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    我们知道,不同肤色的人外貌差别很大,而双胞胎的辨识很难.有意思的是Web服务器/Web容器/Web应用程序服务器/反向代理有点像四胞胎,在网络上经常一起出现.本文将带读者对这四个相似概念如何区分. 1 ...

  10. Windows下使用nginx搭建反向代理服务器

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

随机推荐

  1. osggeometry修改更新顶点

    osg::Geometry *geometry = geode->getDrawable()->asGeometry();geometry->setDataVariance(osg: ...

  2. 130、 Android OkHttp完全解析(转载)

    完全解析:http://blog.csdn.net/lmj623565791/article/details/47911083 从原理角度解析http文件上传 http://blog.csdn.net ...

  3. JavaScript 运行机制详解:Event Loop

    参考地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是 ...

  4. 使用 Sublime Text 将含下划线的字符串批量替换为驼峰命名法格式的字符串

    本文版权归cxun所有,如有转载请注明出处与本文链接,谢谢!原文地址:http://www.cnblogs.com/cxun/p/7762984.html For indexing: Convert ...

  5. Kindeditor视频上传问题处理

    初始的时候,video这个标签kindeditor是不识别的,只要html处理或者全屏的时候,都会被排除掉.如果想要video存在,则需要设置属性filterMode :false 如果只是想要某个标 ...

  6. Zephyr学习(五)线程和调度

    前面说过zephyr支持静态和动态两种方式创建线程,这里分析动态创建的方式.应用程序通过调用k_thread_create()函数创建一个线程,实际上是调用_impl_k_thread_create( ...

  7. Ext-JS-Modern-Demo 面向移动端示例

    基于Ext Js 6.5.2 面向移动端示例,低于此版本可能存在兼容问题,慎用 已忽略编译目录,请自行编译运行 Sencha Cmd 版本:v6.5.2.15 git地址:https://github ...

  8. JBPM工作流(四)——管理流程定义

    概念: ProcessDefinition,流程定义:一个流程的步骤说明,如一个请假流程.报销流程.是一个规则. ProcessDefinition,流程定义对象,是解析.jpdl.xml文件得到流程 ...

  9. hibernate的面试总结

    hibenate的面试总结. 可能现在大家常常还会遇到一个些面试的时候问一些关于hibernate的问题,我个人觉得,这些东西一般做过开发的人在使用上没有任何的问题的,但是如果是要你来说就不一定能够说 ...

  10. juqery 下拉加载数据

    html  代码  一开始是需要显示的第一页 <div class="hot-product f15 fixed-Width clearfix" id="goods ...