为了简化从 Readable Stream 监听 data 事件来获取数据并使用 Writable Stream 的 write() 方法来输出,可以使用 Readable Stream 的 pipe() 方法。那么如何编写 HTTP 反向代理服务器?
为了简化从 Readable Stream 监听 data 事件来获取数据并使用 Writable Stream 的 write() 方法来输出,可以使用 Readable Stream 的 pipe() 方法。那么如何编写 HTTP 反向代理服务器?
简单版本 以下是实现一个简单 HTTP 反向 代理服务器的各个文件和代码(没有任何第三方库依赖), 为了使代码更简洁,使用了一些最新的 ES 语法特性,需要使用 Node v8.x 最新版本来运行 : 文件 proxy.js : const http = require("http"); const assert = require("assert"); const log = require("./log"); /** 反向代理中间件 */ module.exports = function reverseProxy(options) { assert(Array.isArray(options.servers), "options.servers 必须是数组"); assert(options.servers.length > 0, "options.servers 的长度必须大于 0"); // 解析 服务器地址,生成 hostname 和 port const servers = options.servers.map(str => { const s = str.split(":"); return { hostname: s[0], port: s[1] || "80" }; }); // 获取一个后端服务器,顺序循环 let ti = 0; function getTarget() { const t = servers[ti]; ti++; if (ti >= servers.length) { ti = 0; } return t; } // 生成监听 error 事件函数,出错时响应 500 function bindError(req, res, id) { return function(err) { const msg = String(err.stack || err); log("[%s] 发生错误: %s", id, msg); if (!res.headersSent) { res.writeHead(500, { "content-type": "text/plain" }); } res.end(msg); }; } return function proxy(req, res) { // 生成代理请求信息 const target = getTarget(); const info = { ...target, method: req.method, path: req.url, headers: req.headers }; const id = `${req.method} ${req.url} => ${target.hostname}:${target.port}`; log("[%s] 代理请求", id); // 发送代理请求 const req2 = http.request(info, res2 => { res2.on("error", bindError(req, res, id)); log("[%s] 响应: %s", id, res2.statusCode); res.writeHead(res2.statusCode, res2.headers); res2.pipe(res); }); req.pipe(req2); req2.on("error", bindError(req, res, id)); }; }; 文件 log.js : const util = require("util"); /** 打印日志 */ module.exports = function log(...args) { const time = new Date().toLocaleString(); console.log(time, util.format(...args)); }; 说明: log.js 文件实现了一个用于打印日志的函数 log() ,它可以支持 console.log() 一样的用法,并且自动在输出前面加上当前的日期和时间,方便我们浏览日志 reverseProxy() 函数入口使用 assert 模块来进行基本的参数检查,如果参数格式不符合要求即抛出异常,保证可以第一时间让开发者知道,而不是在运行期间发生各种不可预测的错误。 getTarget() 函数用于循环返回一个目标服务器地址 bindError() 函数用于监听 error 事件,避免整个程序因为没有捕捉网络异常而崩溃,同时可以统一返回出错信息给客户端。 以上就是小编对于编写 HTTP 反向代理服务器的建议。