Nodejs之实现代理

栏目: IT技术 · 发布时间: 4年前

内容简介:我们上线一个网站,往往需要代理来完成需求,不然的话就只能使用最简单的做法是拿已经写好的包直接使用上面: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 是一个数组方法,包含 deleteLengthtimeoutXHeadersstream ,关键点在 stream ,其他基本是辅助作用, XHeaders 功能是设置 x-forwarded-* 这种 header ,不过前提是 option 配置了 xfwd 才行, timeout 是设置超时时间的, deleteLength 只有请求方法是 OPTIONSDELETE 才会执行

...
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 ,错误处理也做得很好,已经很成熟了


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Developer's Guide to Social Programming

Developer's Guide to Social Programming

Mark D. Hawker / Addison-Wesley Professional / 2010-8-25 / USD 39.99

In The Developer's Guide to Social Programming, Mark Hawker shows developers how to build applications that integrate with the major social networking sites. Unlike competitive books that focus on a s......一起来看看 《Developer's Guide to Social Programming》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具