内容简介:我们上线一个网站,往往需要代理来完成需求,不然的话就只能使用最简单的做法是拿已经写好的包直接使用上面:point_up_2:代码监听
我们上线一个网站,往往需要代理来完成需求,不然的话就只能使用 IP
加端口的方式来访问应用,目前基本上都会使用 Nginx
来完成反向代理,那么我们开发 Node
应用一定需要 Nginx
吗?肯定不是,我们完全可以通过 Node
来做,但是大多是不建议你用 Node
去处理,因为 Nginx
功能更强大,比如负载均衡,本文主要是帮你了解如何通过 Node
实现代理
使用http-proxy
最简单的做法是拿已经写好的包直接使用
$ cnpm i http-proxy
const proxy = require("http-proxy").createProxyServer({}); server = require("http").createServer(function(req, res) { const host = req.headers.host; switch (host) { case "your domain": proxy.web(req, res, { target: "http://localhost:8000" }); break; default: res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Welcome to my server!"); } }); console.log("listening on port 80"); server.listen(80);
上面:point_up_2:代码监听 80
端口(如果你的服务器目前使用来 Nginx
,暂用 80
端口的话,需要先暂停 Nginx
服务),然后我们通过访问域名(前提是域名做好了解析),然后使用 proxy.web
方法反向代理到当前服务下的 8000
端口,到此一个简单的服务完成了
原生实现
const http = require("http"); const url = require("url"); function request(req, res) { const reqUrl = url.parse(req.url); const target = url.parse("http://localhost:3000"); const options = { hostname: target.hostname, port: target.port, path: reqUrl.path, method: req.method, headers: req.headers }; const proxyReq = http.request(options); proxyReq.on("response", proxyRes => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); }); req.pipe(proxyReq); } http .createServer() .on("request", request) .listen(8003);
是不是很简单?通过访问 8003
端口,我们将请求转发到 3000
端口,可以复制当前代码尝试一下,前提是 3000
端口可以正常访问。当访问 8003
端口的时候,内部重新请求我们需要代理的地址,然后通过 pipe
返回转发后的数据
http-proxy源码实现原理
执行 proxy.web
function ProxyServer(options) { ... this.web = this.proxyRequest = createRightProxy('web')(options); ... }
内部关键代码执行了一下这段, passes
是一个数组方法,包含 deleteLength
、 timeout
、 XHeaders
、 stream
,关键点在 stream
,其他基本是辅助作用, XHeaders
功能是设置 x-forwarded-*
这种 header
,不过前提是 option
配置了 xfwd
才行, timeout
是设置超时时间的, deleteLength
只有请求方法是 OPTIONS
和 DELETE
才会执行
... for(var i=0; i < passes.length; i++) { if(passes[i](req, res, requestOptions, head, this, cbl)) { // passes can return a truthy value to halt the loop break; } }
stream
方法
module.exports = { deleteLength: ...., timeout: ..., XHeaders: ..., stream: function stream(req, res, options, _, server, clb) { // And we begin! server.emit('start', req, res, options.target || options.forward); var agents = options.followRedirects ? followRedirects : nativeAgents; var http = agents.http; var https = agents.https; if(options.forward) { // If forward enable, so just pipe the request var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); // error handler (e.g. ECONNRESET, ECONNREFUSED) // Handle errors on incoming request as well as it makes sense to var forwardError = createErrorHandler(forwardReq, options.forward); req.on('error', forwardError); forwardReq.on('error', forwardError); (options.buffer || req).pipe(forwardReq); if(!options.target) { return res.end(); } } // Request initalization var proxyReq = (options.target.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); // Enable developers to modify the proxyReq before headers are sent proxyReq.on('socket', function(socket) { if(server) { server.emit('proxyReq', proxyReq, req, res, options); } }); // allow outgoing socket to timeout so that we could // show an error page at the initial request if(options.proxyTimeout) { proxyReq.setTimeout(options.proxyTimeout, function() { proxyReq.abort(); }); } // Ensure we abort proxy if request is aborted req.on('aborted', function () { proxyReq.abort(); }); // handle errors in proxy and incoming request, just like for forward proxy var proxyError = createErrorHandler(proxyReq, options.target); req.on('error', proxyError); proxyReq.on('error', proxyError); function createErrorHandler(proxyReq, url) { return function proxyError(err) { if (req.socket.destroyed && err.code === 'ECONNRESET') { server.emit('econnreset', err, req, res, url); return proxyReq.abort(); } if (clb) { clb(err, req, res, url); } else { server.emit('error', err, req, res, url); } } } (options.buffer || req).pipe(proxyReq); proxyReq.on('response', function(proxyRes) { if(server) { server.emit('proxyRes', proxyRes, req, res); } if(!res.headersSent && !options.selfHandleResponse) { for(var i=0; i < web_o.length; i++) { if(web_o[i](req, res, proxyRes, options)) { break; } } } if (!res.finished) { // Allow us to listen when the proxy has completed proxyRes.on('end', function () { if (server) server.emit('end', req, res, proxyRes); }); // We pipe to the response unless its expected to be handled by the user if (!options.selfHandleResponse) proxyRes.pipe(res); } else { if (server) server.emit('end', req, res, proxyRes); } }); } }
关键代码
// Request initalization var proxyReq = (options.target.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); (options.buffer || req).pipe(proxyReq); proxyReq.on('response', function(proxyRes) { if(!res.headersSent && !options.selfHandleResponse) { for(var i=0; i < web_o.length; i++) { if(web_o[i](req, res, proxyRes, options)) { break; } } } if (!res.finished) { // We pipe to the response unless its expected to be handled by the user if (!options.selfHandleResponse) proxyRes.pipe(res); } });
实现大致和我们之前写得差不多,但是他考虑得更多,支持 https
,错误处理也做得很好,已经很成熟了
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 动态代理三部曲(一):动态代理模式及实现原理
- 使用 Javassist 实现动态代理
- Javassist实现JDK动态代理
- 代理模式-go/java实现
- 中通安全访问代理设计与实现
- vue配置文件实现代理v2版本
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。