如何编网站的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 反向代理服务器的建议。
扫码访问手机页面