要说到反向代理,很多人第一时间想到的是nginx,没错,在这个领域做得最好的,我觉得应该算是nginx,为什么想用nodejs来实现?处于一种研究的心态和让反向代理更好的为自己服务,毕竟要修改nginx还需要c c++等这些方面的知识,而且我对nodejs更为熟悉一点(说白了就是不懂C++…..)。
说到反向代理,那首先要明白什么是反向代理,我们一般用代理就是我要访问一个网站,但是直接访问不了,然后通过代理服务器访问,然后代理服务器再将信息返回给我,代理的这个行为是发生在客户端。反向代理则刚好相反,代理服务接到了各种网络上的各种请求(比如我访问网址),然后根据请求查找到内部服务器,再将信息返回到请求的一方(我),代理的行为是发生在服务端。其实这两者发生的情景是不一样的,正向代理通常是我们要访问一个国外网站,但是直接访问不了,要经过代理服务器来实现,而反向代理通常是用来管理内部的多台服务器,实现负载均衡等。
那么用了方向代理就不得不说虚拟路径,大中型的网站中的静态资源是分离出来的,如果通过像apache/tomcat/iis等这些服务器来访问静态资源,那么效率会降低不少,最好的方法就是直接读取,所以用到了虚拟路径,虚拟路径的作用是你访问a.com/res/index.js的时候,直接指向服务器目录下的res/index.js,直接进行硬盘的IO读写,绕过了动态服务器的解析,那么实现反向代理就是从这个静态资源的分离开始,毕竟前端最关心的还是自己的静态资源领地。
那么经过上面的分析,我们可以通过nodejs中的http-proxy模块来实现,在使用这个之前要做的是对请求类型的一些判断,即请求Js/css/jpg等这些文件时,返回的response信息类型是什么,于是有了下面的代码(也是我自己写的一个content-type模块)content-type.js:
/*
content type
by subying tearlight2008@gmail.com
根据后缀 转换 content-type
*/
var getContentType = function(ext){
var contentType = '';
switch(ext){
case ".html":
contentType= "text/html";
break;
case ".js":
contentType="text/javascript";
break;
case ".css":
contentType="text/css";
break;
case ".gif":
contentType="image/gif";
break;
case ".jpg":
contentType="image/jpeg";
break;
case ".png":
contentType="image/png";
break;
case ".ico":
contentType="image/icon";
break;
default:
contentType="application/octet-stream";
}
return contentType;
};
module.exports = getContentType;
有了这个还需要用到nodejs里面的一个模块:http-proxy,npm地址为 https://www.npmjs.org/package/http-proxy。那么前面有说过,将静态资源分离,就会用到虚拟目录,要怎么判断它是静态资源呢?这就是我为什么要用nodejs实现反向代理的原因了,因为我可以自己自由的定义,就是一个if的问题而已。本人习惯将静态资源放到了一个res的文件夹里,所以我只要判断请求里面存在’/res/’这个目录,我就知道是请求静态资源了。判断是请求静态资源后,再找到项目的物理路径比如’E:\teset\res’,再用fs模块读取出来,返回到客户端。那么其他的请求就通过本地的其他服务器来实现。本例中用到的代理方法是http-proxy的web方法,http-proxy更多的用法可以去查阅上面给出来的npm地址。
/*
http-proxy
*/
var http = require('http')
,httpProxy = require('http-proxy') //http-proxy
,proxy = httpProxy.createProxyServer({})
,fs = require('fs')
,path = require('path')
,getConType = require('./content-type')//自己定义 根据后缀转换Content-Type
;
var server = http.createServer(function(req, res) {
var _url = req.url //获取请求的url
,_file
,_localPath
,_localFile
,_ext
,_stream
;
if(_url.indexOf('/res/')>-1){
_file = _url.replace(/\?.*/ig,'');
_ext = path.extname(_file); // 文件扩展
//判断是否为js文件
if(_ext === '.js'){
_file = _file.replace('res/js/','res/src_js/');
}
//转换成本地路径
_localPath = 'E:/web/angularjs/';
_localFile = _localPath+_file;
//判断文件是否存在
if(fs.existsSync(_localFile)){//如果文件存在
res.writeHead(200, {"Content-Type": getConType(_ext) });
_stream = fs.createReadStream(_localFile, {flags : "r", encoding : null});//只读模式 读取文件内容
_stream.on('error',function(){//如果读取错误 返回404
res.writeHead(404, {"Content-Type": "text/html"});
res.end("<h1>404 Read Error</h1>");
});
_stream.pipe(res);//连接文件流和http返回流的管道,用于返回实际Web内容
_stream.on('end',function(){
_stream = null;
})
}else{//返回404错误
res.writeHead(404, {"Content-Type": "text/html"});
res.end("<h1>404 Not Found</h1>");
}
}else{
proxy.web(req, res, { target: 'http://127.0.0.1:81' });
}
});
console.log("listening on port 80")
server.listen(80);
总结来说,实现反向代理的静态资源分离,首先是判断request来判断是否请求静态文件,如果是请求静态文件,就找到项目下的具体文件,直接用fs读取,如果不是请求静态文件那么就用proxy.web来请求内部的服务器(我的是127.0.0.1:81)来实现。希望给学习nodejs实现反向代理的朋友一些帮助,高手莫笑, ,正式的项目中还是用nginx比较好!