如果您经常使用Node.js编写Web服务方法,那么您必须熟悉使用Nginx作为代理服务。在目前的消费情况下,我们往往需要在内网的多个服务器上布置法式风格,而在一个多核服务器中,为了充实和 *** 纵所有的CPU资本,我们还需要启动多个服务进程,这些服务进程需要监控差异。然后,Nginx被用作代理服务器,来自用户阅读器的请求被接收并被转发到所有的Web服务器。下图显示了事情的流程:
Node.js上显示了一个简单的http代理,法语代理的长度通常很简单。本文中例子的中心代码只需要60多个,你只需要知道内置HTTP模块的基本用法。详情请看下文。
取创意,取相关技术。
使用http.createServer()创建的http服务器,处理请求的函数模式一般是function(req,res){}(以下简称requestHandler),它接收两个参数,即HTTP。IncomingMessage和http。服务器响应工具。我们可以通过这两个工具获得所有关于请求的疑问,并停止对它们的响应。
一般来说,支流Node.jsWeb框架的中心组件(像connect)有两种情况:
如果中心不需要任何初始化参数,它的导出结果是requestHandler。
如果需要中心零件的初始化参数,其导出结果就是中心零件的初始化函数。实现初始化函数时,会传入一个选项工具,实现后会返回一个requestHandler。
为了使代码更加标准,在本例中,我们将反向代理方法想象为一种中间件模式,并利用上述第二种连接方式:
//作为中心件而死
consthandler=reverseProxy({
//初始化参数,用于设置目的服务器列表。
服务器:["127.0.0.1:3001","127.0.0.1:3002","127.0.0.1:3003"]
});
//可以在http模块中间接使用
constserver=http.createserver(handler);
//作为核心部分,它被用在连接模块中
app.use(处理程序);
澄清:
在中的代码中,reverseProxy是代理服务器中心的初始化函数,它带有一个工具参数。servers是后端服务器位置的列表,每个位置都是IPlocation:end-center。
执行reverseProxy()后,会返回一个类似function(req,res){}的函数,用于处理HTTP请求。可以作为connect中间件的http.createServer()和app.use()的处理函数。
当接收到来自客户端的请求时,根据第二周期从服务器阵列中选择服务器位置,并将请求代理发送到该位置的服务器。
服务器收到HTTP请求后,需要向作为代理的目标服务器发起新的HTTP请求,并可以使用http.request()接收请求:
constreq=http.request(
{
主机名:“目标服务器位置”,
端口:“80”,
路径:“恳求方式”,
标题:{
《x-y-z》:“乞讨头”
}
},
功能(资源){
//res是一个echo工具。
console.log(RES.statuscode);
}
);
//如果需要收集一个恳求者,用write()打end()
req.end();
要将客户的整个恳求者(正文部门,POST和PUT中会有恳求者)转移到另一个服务器,可以使用Stream工具的pipe()方法,例如:
//req和res是对客户的全部恳求和回应工具。
//req2warningres2作为服务器主动方代理的恳求工具。
//将数据传输到req2。
req.pipe(请求2);
//将数据从res2传输到res
res2.pipe(RES);
澄清:
req工具是一个可读的流,通过数据加扰的过程接收数据,当到达数据加扰结束时,表示数据接收结束。
Res工具是一个可写流(WritableStream),通过write()的过程输出数据,由end()完成输出。
为了通过监视来自可读流的数据并使用可写流的write()方法输出数据来简化数据的获取,我们可以使用可读流的pipe()方法。
以上只是提到了实现HTTP代理需求的关键技术。具体文档请参考以下地方:https://nodejs.org/api/http.html#http_http_request_options_callback。
当然,为了实现一种友谊,我们往往需要很多额外的东西。详情请看下文。
简单版本
下面是一个简单HTTP反向代理服务器的实际文件战代码(有任何第三轮库可以依赖)。为了使代码更加简洁,使用了一些最新的ES语法特性,需要运行最新版本的Nodev8.x:
文件proxy.js:
consthttp=require("http");
constassert=require("assert");
constlog=require("。/log");
/**充当背面代理的中心部件*/
module.exports=functionreverseproxy(选项){
assert(array.isarray(options.servers),“options.servers必须是数组”);
assert(options.servers.length>;0,“options.servers的最小程度必须大于0”);
//解析服务器位置,死磕主机名和端口。
constservers=options.servers.map(str=>;{
consts=str.split(":");
return{hostname:s[0],port:s[1]||"80"};
});
//弄个后端服务器,第二个循环。
设ti=0;
函数getTarget(){
constt=servers[ti];
ti++;
if(ti>;=服务器.长度){
ti=0;
}
returnt;
}
//Die监控错误事故功能,下降时回显500。
函数绑定错误(req,res,id){
返回函数(错误){
constmsg=String(err.stack||err);
日志("[%s]攻击:%s",id,msg);
如果(!RES.headersent){
res.writeHead(500,{"content-type":"text/plain"});
}
res.end(消息);
};
}
返回函数代理(req,res){
//代理,求疑息
consttarget=gettarget();
常量信息={
...目标,
方法:req.method
路径:req.url,
头:req.headers
};
constid=`${req.method}${req.URL}=>;${target.hostname}:${target.port}`;
日志("[%s]代理抗辩",id);
//收款代理人的代理抗辩
constreq2=http.request(info,res2=>{
res2.on("error",bindError(req,res,id));
Log("[%s]echo:%s",id,res2.statuscode);
res.writeHead(res2.statusCode,res2.headers);
res2.pipe(RES);
});
req.pipe(请求2);
req2.on("error",bindError(req,res,id));
};
};
log.js文件:
constutil=require("util");
/**打印日记*/
module.exports=函数日志(...args){
consttime=新日期()。toLocaleString();
console.log(time,util.format(...args));
};
澄清:
log.js文件真正展示了一个用来打印日记的函数log()。可以支持console.log()的相同用法,在输出前主动减少被骗前的日期和战争时间,方便我们看日记。
reverseProxy()函数导入使用assert模块来停止基本参数检查。如果参数模式不符合要求,它将被抛出。可以保证接收方第一时间知道,而不是在运行期间出现各种不可预知的问题。
get()函数用于递归返回目标服务器的位置。
BindError()函数用于监控错误无序,防止所有法式水果因抓取和收集而崩溃,同时可以向客户端返回损坏的信息。
为了测试代码 *** 作的结果,我编写了一个简单的法语版本,文件server.js:
consthttp=require("http");
constlog=require("。/log");
constreverseProxy=require("。/proxy");
//创建反向代理服务器。
函数startProxyServer(端口){
返回新承诺((resolve,reject)=>;{
constserver=http.createServer(
反向Proxy({
服务器:["127.0.0.1:3001","127.0.0.1:3002","127.0.0.1:3003"]
})
);
server.listen(port,()=>{
Log("背靠背代理服务器已启动:%s",端口);
解析(服务器);
});
server.on("错误",拒绝);
});
}
//创建演示服务器
函数开始示例服务器(端口){
返回新承诺((解决,拒绝)=>{
constserver=http.createserver(function(req,res){
constchunks=[];
req.on("data",chunk=>chunks.push(chunk));
req.on("end",()=>{
constbuf=buffer.concat(chunks);
RES.end(`${port}:${req.method}${req.URL}${buf.tostring()}`.trim());
});
});
server.listen(port,()=>{
Log("服务器启动:%s",端口);
解析(服务器);
});
server.on("错误",拒绝);
});
}
(异步函数(){
awaitstartExampleServer(3001);
awaitstartExampleServer(3002);
awaitstartExampleServer(3003);
awaitstartProxyServer(3000);
})();
执行以下命令开始:
nodeserver.js
然后,我们可以通过processcurl命令检查返回的结果:
curlhttp://127.0.0.1:3000/hello/world
重复执行命令,如果输出结果是偶然的,应该是这样的(输出内容结束部门要按照第二个周期):
3001:GET/hello/world
3002:GET/hello/world
3003:GET/hello/world
3001:GET/hello/world
3002:GET/hello/world
3003:GET/hello/world
注意:如果使用阅读器打开网站,可以看到不同的结果。因此,读者会主动测试考试请求/favicon。事实上,一旦页面被更新,就会收到两个请求。
单元测试
上面,我们已经完成了一个基本的HTTP反向代理方法,并通过一个简单的过程验证了它可以做普通的事情。但是我们没有足够的测试,比如只验证GET请求,验证POST请求等恳求方式。而且通过流程步法做更多的测试,费力不讨好,干脆省略。所以,接下来我们要从中减去活动单元测试。
本文选择Node.js世界中广泛使用的mocha作为单元测试框架,反汇编使用supertest停止HTTP连接请求的测试。因为supertest以前带了一些基本面的断行方法,所以暂时不需要柴应该的第三圈断行库。
最后,npminit初始化一个package.json文件,然后执行以下命令:devicemocha和supertest:
npm安装mocha超级测试-保存-开发
然后创建一个新文件test.js:
consthttp=require("http");
constlog=require("。/log");
constreverseProxy=require("。/proxy");
const{expect}=require("chai");
constrequest=require("supertest");
//创建反向代理服务器。
函数startProxyServer(){
返回新承诺((resolve,reject)=>;{
constserver=http.createServer(
反向Proxy({
服务器:["127.0.0.1:3001","127.0.0.1:3002","127.0.0.1:3003"]
})
);
Log("备份代理的代理服务器已经启动");
解析(服务器);
});
}
//创建演示服务器
函数开始示例服务器(端口){
返回新承诺((解决,拒绝)=>{
constserver=http.createserver(function(req,res){
constchunks=[];
req.on("data",chunk=>chunks.push(chunk));
req.on("end",()=>{
constbuf=buffer.concat(chunks);
RES.end(`${port}:${req.method}${req.URL}${buf.tostring()}`.trim());
});
});
server.listen(port,()=>{
Log("服务器启动:%s",端口);
解析(服务器);
});
server.on("错误",拒绝);
});
}
Describe("测试后台代理",function(){
let服务器;
让exampleservers=[];
//测试开始前启动服务器。
before(异步函数(){
exampleservers.push(awaitstartexampleserver(3001));
exampleservers.push(awaitstartexampleserver(3002));
exampleservers.push(awaitstartexampleserver(3003));
server=awaitstartProxyServer();
});
//测试结束后关闭服务器。
after(异步函数(){
for(示例服务器的常量服务器){
server.close();
}
});
It("第二周期返回目的地",asyncfunction(){
等待请求(服务器)
。get("/hello")
。预期(200)
。expect(`3001:GET/hello`);
等待请求(服务器)
。get("/hello")
。预期(200)
。expect(`3002:GET/hello`);
等待请求(服务器)
。get("/hello")
。预期(200)
。expect(`3003:GET/hello`);
等待请求(服务器)
。get("/hello")
。预期(200)
。expect(`3001:GET/hello`);
});
It("支持POST请求",异步函数(){
等待请求(服务器)
。发布("/xyz")
。发送({
甲:123,
乙:456
})
。预期(200)
。expect(`3002:POST/xyz{"a":123,"b":456}`);
});
});
澄清:
就在单元测试开始之前,需要通过processbefore()注册回调函数,以便在测试用例正在执行的时候先启动服务器。
同样,通过processafter()注册回调函数,这样就可以在所有测试用例正在执行之后关闭服务器释放资金(否则mocha进程不会退出)。
使用supertest接收请求时,代理服务器不需要监听客户端,只需要使用服务器实例作为挪用参数即可。
然后修复package.json文件的脚本部分:
{
"脚本":{
"test":"mochatest.js"
}
}
执行以下命令开始测试:
npm测试
如果一切正常,我们应该会看到这样的输出结果,其中类似passing的提醒暗示我们的测试已经经历了整个过程:
测试代理代理
2017-12-1218:28:15服务器启动时间:3001
2017-12-1218:28:15服务器启动时间:3002
2017-12-1218:28:15服务器启动时间:3003
2017-12-1218:28:15,后台代理的代理服务器已经启动。
2017-12-1218:28:15[GET/hello=>代理人的代理辩护
2017-12-1218:28:15[GET/hello=>回声:200
2017-12-1218:28:15[GET/hello=>为代理人辩护
2017-12-1218:28:15[GET/hello=>回声:200
2017-12-1218:28:15[GET/hello=>代理人的代理辩护
2017-12-1218:28:15[GET/hello=>回声:200
2017-12-1218:28:15[GET/hello=>代理人的代理辩护
2017-12-1218:28:15[GET/hello=>回声:200
第二轮返回目的地
2017-12-1218:28:15[POST/xyz=>为代理人辩护
2017-12-1218:28:15[POST/xyz=>回声:200
支持后恳求
2次通过(45毫秒)
虽然上面的测试代码还不够,剩下的就留给读者了。
心脏连接改善
如果我们想想象一个通用的背靠背代理,我们可以提供一个作为http死去的函数。ClientRequest通过进程实现代理的静态修正请求:
反向Proxy({
服务器:["127.0.0.1:3001","127.0.0.1:3002","127.0.0.1:3003"],
请求:函数(req,info){
//info是一个死请求选项工具。
//我们可以静态删除恳求头,像当前的恳求时间戳。
info.headers["X-Request-Timestamp"]=date.now();
//返回http。客户端请求工具
返回http.request(info);
}
});
然后,原来的http.request(info,(res2)=>:{})部门可以更改为监视对紧急事件的响应:
constreq2=http.request(options.request(info));
req2.on("response",res2=>{});
同样,我们也可以通过流程提供一个函数来纠正部门的回声内容:
反向Proxy({
服务器:["127.0.0.1:3001","127.0.0.1:3002","127.0.0.1:3003"],
响应:函数(res,info){
//info是收款代理在代理征集时使用的请求选项工具。
//我们可以静态设置一些echo头,比如练习代理代理的模板服务器的位置。
RES.setheader("X-backend-Server",`${info.hostname}:${info.port}`);
}
});
这里只收集我的想法,关于详细的方法和代码就不赘述了。
摘要
主要介绍如何使用内置的http模块创建HTTP服务器,发起HTTP请求,并简单介绍如何停止测试HTTP连接。在HTTP恳求代理过程中,主要使用流工具的pipe()方法,关键部门代码只需要检查几次。Node.js中的很多法式风格都采用了Stream的思想,将数据视为一个流,通过使用管道将一个流转换成另一个流。可见Stream在Node.js中的重要性。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)