- // 生成中间件
- const handler = reverseProxy({
- // 初始化参数,用于设置目标服务器列表
- servers: ["127.0.0.1:3001", "127.0.0.1:3002", "127.0.0.1:3003"]
- });
- // 可以直接在 http 模块中使用
- const server = http.createServer(handler);
- // 作为中间件在 connect 模块中使用
- app.use(handler);
- const req = http.request(
- {
- hostname: "目标服务器地址",
- port: "80",
- path: "请求路径",
- headers: {
- "x-y-z": "请求头"
- }
- },
- function(res) {
- // res 为响应对象
- console.log(res.statusCode);
- }
- );
- // 如果有请求体需要发送,使用 write() 和 end()
- req.end();
- // req 和 res 为客户端的请求和响应对象
- // req2 和 res2 为服务器发起的代理请求和响应对象
- // 将 req 收到的数据转发到 req2
- req.pipe(req2);
- // 将 res2 收到的数据转发到 res
- res2.pipe(res);
- 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));
- };
- };
- const util = require("util");
- /** 打印日志 */
- module.exports = function log(...args) {
- const time = new Date().toLocaleString();
- console.log(time, util.format(...args));
- };
- const http = require("http");
- const log = require("./log");
- const reverseProxy = require("./proxy");
- // 创建反向代理服务器
- function startProxyServer(port) {
- return new Promise((resolve, reject) => {
- const server = http.createServer(
- reverseProxy({
- servers: ["127.0.0.1:3001", "127.0.0.1:3002", "127.0.0.1:3003"]
- })
- );
- server.listen(port, () => {
- log("反向代理服务器已启动: %s", port);
- resolve(server);
- });
- server.on("error", reject);
- });
- }
- // 创建演示服务器
- function startExampleServer(port) {
- return new Promise((resolve, reject) => {
- const server = http.createServer(function(req, res) {
- const chunks = [];
- req.on("data", chunk => chunks.push(chunk));
- req.on("end", () => {
- const buf = Buffer.concat(chunks);
- res.end(`${port}: ${req.method} ${req.url} ${buf.toString()}`.trim());
- });
- });
- server.listen(port, () => {
- log("服务器已启动: %s", port);
- resolve(server);
- });
- server.on("error", reject);
- });
- }
- (async function() {
- await startExampleServer(3001);
- await startExampleServer(3002);
- await startExampleServer(3003);
- await startProxyServer(3000);
- })();
- node server.js
- curl http://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
- npm install mocha supertest--save - dev
- const http = require("http");
- const log = require("./log");
- const reverseProxy = require("./proxy");
- const {
- expect
- } = require("chai");
- const request = require("supertest");
- // 创建反向代理服务器
- function startProxyServer() {
- return new Promise((resolve, reject) = >{
- const server = http.createServer(reverseProxy({
- servers: ["127.0.0.1:3001", "127.0.0.1:3002", "127.0.0.1:3003"]
- }));
- log("反向代理服务器已启动");
- resolve(server);
- });
- }
- // 创建演示服务器
- function startExampleServer(port) {
- return new Promise((resolve, reject) = >{
- const server = http.createServer(function(req, res) {
- const chunks = [];
- req.on("data", chunk = >chunks.push(chunk));
- req.on("end", () = >{
- const buf = Buffer.concat(chunks);
- res.end(`$ {
- port
- }: $ {
- req.method
- }
- $ {
- req.url
- }
- $ {
- buf.toString()
- }`.trim());
- });
- });
- server.listen(port, () = >{
- log("服务器已启动: %s", port);
- resolve(server);
- });
- server.on("error", reject);
- });
- }
- describe("测试反向代理",
- function() {
- let server;
- let exampleServers = [];
- // 测试开始前先启动服务器
- before(async
- function() {
- exampleServers.push(await startExampleServer(3001));
- exampleServers.push(await startExampleServer(3002));
- exampleServers.push(await startExampleServer(3003));
- server = await startProxyServer();
- });
- // 测试结束后关闭服务器
- after(async
- function() {
- for (const server of exampleServers) {
- server.close();
- }
- });
- it("顺序循环返回目标地址", async
- function() {
- await request(server).get("/hello").expect(200).expect(`3001 : GET / hello`);
- await request(server).get("/hello").expect(200).expect(`3002 : GET / hello`);
- await request(server).get("/hello").expect(200).expect(`3003 : GET / hello`);
- await request(server).get("/hello").expect(200).expect(`3001 : GET / hello`);
- });
- it("支持 POST 请求", async
- function() {
- await request(server).post("/xyz").send({
- a: 123,
- b: 456
- }).expect(200).expect(`3002 : POST / xyz {
- "a": 123,
- "b": 456
- }`);
- });
- });
- {
- "scripts": {
- "test": "mocha test.js"
- }
- }
- npm test
- 测试反向代理
- 2017-12-12 18:28:15 服务器已启动: 3001
- 2017-12-12 18:28:15 服务器已启动: 3002
- 2017-12-12 18:28:15 服务器已启动: 3003
- 2017-12-12 18:28:15 反向代理服务器已启动
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3001] 代理请求
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3001] 响应: 200
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3002] 代理请求
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3002] 响应: 200
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3003] 代理请求
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3003] 响应: 200
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3001] 代理请求
- 2017-12-12 18:28:15 [GET /hello => 127.0.0.1:3001] 响应: 200
- ✓ 顺序循环返回目标地址
- 2017-12-12 18:28:15 [POST /xyz => 127.0.0.1:3002] 代理请求
- 2017-12-12 18:28:15 [POST /xyz => 127.0.0.1:3002] 响应: 200
- ✓ 支持 POST 请求
- 2 passing (45ms)
- reverseProxy({
- servers: ["127.0.0.1:3001", "127.0.0.1:3002", "127.0.0.1:3003"],
- request: function(req, info) {
- // info 是默认生成的 request options 对象
- // 我们可以动态增加请求头,比如当前请求时间戳
- info.headers["X-Request-Timestamp"] = Date.now();
- // 返回 http.ClientRequest 对象
- return http.request(info);
- }
- });
- const req2 = http.request(options.request(info));
- req2.on("response", res2 = >{});
- reverseProxy({
- servers: ["127.0.0.1:3001", "127.0.0.1:3002", "127.0.0.1:3003"],
- response: function(res, info) {
- // info 是发送代理请求时所用的 request options 对象
- // 我们可以动态设置一些响应头,比如实际代理的模板服务器地址
- res.setHeader("X-Backend-Server", `${info.hostname}:${info.port}`);
- }
- });
来源: https://juejin.im/entry/5a30a0ba51882535cd4ad12e