TT Bigdata TT Bigdata
首页
  • 部署专题

    • 常规安装
    • 一键部署
  • 组件专题

    • 安装指导
    • 魔改分享
  • 高阶玩法

    • 实战 Kerberos
    • 实战 Ranger
  • 版本专题

    • 更新说明
    • BUG临时处理
  • 实验室

    • VIEW插件
    • JIRA速查
  • Ambari-Env

    • 环境准备
    • 开始使用
  • 二开指导

    • 前端开发
    • 后端开发
  • 组件编译

    • 专区—Ambari
    • 专区—Bigtop-官方组件
    • 专区—Bigtop-扩展组件
  • 报错解决

    • 专区—Ambari
    • 专区—Bigtop
  • 其他技巧

    • APT仓库增量更新
    • Maven镜像加速
    • Gradle镜像加速
    • Bower镜像加速
    • 虚拟环境思路
    • R环境安装+一键安装脚本
    • Ivy配置私有镜像仓库
    • Node.js 多版本共存方案
    • Ambari Web本地启动
    • Npm镜像加速
    • PostgreSQL快速安装
    • Temurin JDK 23快速安装
  • 成神之路

    • 专区—Ambari
    • 专区—Ambari-Metrics
    • 专区—Bigtop
  • 集成案例

    • Redis集成教学
    • Dolphin集成教学
    • Doris集成教学
    • 持续整理...
  • 核心代码

    • 各组件代码
    • 通用代码模板
  • 国产化&其他系统

    • Kylin V10系列
    • Rocky系列
    • Ubuntu系列
  • Grafana监控方案

    • Ambari-Metrics插件
    • Infinity插件
  • 优化增强

    • 组件配置调优
  • 支持&共建

    • 蓝图愿景
    • 合作共建
    • 服务说明
GitHub (opens new window)

JaneTTR

数据酿造智慧,每一滴都是沉淀!
首页
  • 部署专题

    • 常规安装
    • 一键部署
  • 组件专题

    • 安装指导
    • 魔改分享
  • 高阶玩法

    • 实战 Kerberos
    • 实战 Ranger
  • 版本专题

    • 更新说明
    • BUG临时处理
  • 实验室

    • VIEW插件
    • JIRA速查
  • Ambari-Env

    • 环境准备
    • 开始使用
  • 二开指导

    • 前端开发
    • 后端开发
  • 组件编译

    • 专区—Ambari
    • 专区—Bigtop-官方组件
    • 专区—Bigtop-扩展组件
  • 报错解决

    • 专区—Ambari
    • 专区—Bigtop
  • 其他技巧

    • APT仓库增量更新
    • Maven镜像加速
    • Gradle镜像加速
    • Bower镜像加速
    • 虚拟环境思路
    • R环境安装+一键安装脚本
    • Ivy配置私有镜像仓库
    • Node.js 多版本共存方案
    • Ambari Web本地启动
    • Npm镜像加速
    • PostgreSQL快速安装
    • Temurin JDK 23快速安装
  • 成神之路

    • 专区—Ambari
    • 专区—Ambari-Metrics
    • 专区—Bigtop
  • 集成案例

    • Redis集成教学
    • Dolphin集成教学
    • Doris集成教学
    • 持续整理...
  • 核心代码

    • 各组件代码
    • 通用代码模板
  • 国产化&其他系统

    • Kylin V10系列
    • Rocky系列
    • Ubuntu系列
  • Grafana监控方案

    • Ambari-Metrics插件
    • Infinity插件
  • 优化增强

    • 组件配置调优
  • 支持&共建

    • 蓝图愿景
    • 合作共建
    • 服务说明
GitHub (opens new window)
  • Ambari-Web

  • 实战核心

    • Brunch Dev Server 反向代理 API
      • 一、使用方式
        • 1、文件放置位置
        • 2、参数与行为速查
      • 二、brunch-server.js(反向代理版)
      • 三、使用示例与常见联调姿势
        • 1、环境变量覆盖(多环境切换)
        • 2、代理是否生效:两步验证
      • 四、重新启动即可生效
  • META-DEVELOP-FRONTEND
  • 实战核心
JaneTTR
2026-02-25
目录

Brunch Dev Server 反向代理 API

# 一、使用方式

目标

让 Ambari-Web(Brunch)本地开发服务具备“后端反向代理”能力:

  • 页面资源:仍由 http://localhost:3333 提供
  • API 请求:统一代理到远端 Ambari Server(避免跨域、保持调用路径一致)
  • WebSocket:支持 upgrade 透传(便于联调实时能力)

# 1、文件放置位置

下面的内容保存至:

  • ambari-web/brunch-server.js
  • 与 brunch-config.js 保持同一层目录

image-20260225101311041

# 2、参数与行为速查

项目 默认值 作用
Dev Server 端口 3333 Ambari-Web 本地访问入口
代理后端 http://dev1.test.com:8080 Ambari Server 地址(可用环境变量覆盖)
代理前缀 /api 命中此前缀的请求才会走代理(可用环境变量覆盖)
CORS 默认开启 给本地调试兜底(可通过 noCors 关闭)
WS 支持 开启 upgrade 请求命中前缀时透传

建议

Ambari-Web 的请求路径通常围绕 /api/v1/...,所以把 AMBARI_PROXY_PREFIX 设为 /api 最省心:前端代码完全不用改。

# 二、brunch-server.js(反向代理版)

说明

这份 server 文件做了几件事:

  1. 使用 http-proxy 代理匹配前缀的请求到 Ambari Server
  2. 非 API 请求继续走静态资源服务(Brunch 构建产物)
  3. upgrade 事件处理 WebSocket 透传
  4. 代理错误返回 502(避免页面端“无响应”难排查)
/**
 * 版权所有 (c) JaneTTR 2025
 * 项目名称:ambari-env
 * 11本文件属于付费部分代码,仅供个人学习和研究使用。
 *
 * 禁止行为:
 * 1. 未经授权,不得将本文件或其编译后的代码用于任何商业用途;
 * 2. 禁止重新分发本文件或其修改版本;
 * 3. 禁止通过反编译、反向工程等手段试图绕过授权验证。
 *
 * 商业授权:
 * 如需将本文件或其编译后的代码用于商业用途,必须获得版权所有者的书面授权。
 * 联系方式:
 * 邮箱:3832514048@qq.com
 *
 * 责任声明:
 * 本文件按“现状”提供,不附带任何形式的担保,包括但不限于适销性、特定用途适用性或无侵权的担保。
 * 如有任何疑问,请联系版权所有者。
 */


'use strict';

var http = require('http');
var httpProxy = require('http-proxy');
var sysPath = require('path');
var url = require('url');
var express;

try {
    // Prefer the dependency that ships with pushserve (same runtime as brunch default server).
    express = require('pushserve/node_modules/express');
} catch (error) {
    express = require('express');
}

function normalizePath(pathname) {
    return pathname || '/';
}

function ignoreSocketError() {
    // Intentionally ignore transient socket errors like ECONNRESET from upstream disconnects.
}

function startServer(options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }

    options = options || {};
    callback = callback || function () {
    };

    if (options.path == null) options.path = '.';
    if (options.port == null) options.port = 3333;
    if (options.base == null) options.base = '/';
    if (options.indexPath == null) options.indexPath = sysPath.join(options.path, 'index.html');
    if (options.noCors == null) options.noCors = false;
    if (options.noPushState == null) options.noPushState = false;
    if (options.noLog == null) options.noLog = false;

    var proxyTarget = process.env.AMBARI_PROXY_TARGET || options.proxyTarget || 'http://dev1.test.com:8080';
    var proxyPrefix = process.env.AMBARI_PROXY_PREFIX || options.proxyPrefix || '/api';
    proxyPrefix = proxyPrefix.charAt(0) === '/' ? proxyPrefix : '/' + proxyPrefix;

    var app = express();
    var proxy = httpProxy.createProxyServer({
        target: proxyTarget,
        changeOrigin: true,
        ws: true,
        xfwd: true
    });

    function isProxyRequest(pathname) {
        var normalizedPath = normalizePath(pathname);
        return normalizedPath === proxyPrefix || normalizedPath.indexOf(proxyPrefix + '/') === 0;
    }

    proxy.on('error', function (error, request, response) {
        if (response && !response.headersSent) {
            response.writeHead(502, {'Content-Type': 'application/json'});
            response.end(JSON.stringify({
                message: 'Proxy request failed',
                target: proxyTarget,
                details: error && error.message ? error.message : 'Unknown proxy error'
            }));
            return;
        }
        if (request && request.socket) {
            request.socket.destroy();
        }
    });

    if (!options.noCors) {
        app.use(function (request, response, next) {
            response.header('Cache-Control', 'no-cache');
            response.header('Access-Control-Allow-Origin', '*');
            next();
        });
    }

    app.use(function (request, response, next) {
        if (request && request.socket && typeof request.socket.on === 'function' && !request.socket.__ambariProxyErrorHandled) {
            request.socket.__ambariProxyErrorHandled = true;
            request.socket.on('error', ignoreSocketError);
        }
        if (isProxyRequest(url.parse(request.url).pathname)) {
            proxy.web(request, response);
            return;
        }
        next();
    });

    app.use(options.base, express.static(sysPath.resolve(options.path)));

    if (!options.noPushState) {
        app.all('*', function (request, response) {
            response.sendfile(options.indexPath);
        });
    }

    var server = http.createServer(app);

    server.on('upgrade', function (request, socket, head) {
        if (socket && typeof socket.on === 'function' && !socket.__ambariProxyErrorHandled) {
            socket.__ambariProxyErrorHandled = true;
            socket.on('error', ignoreSocketError);
        }
        if (isProxyRequest(url.parse(request.url).pathname)) {
            proxy.ws(request, socket, head);
            return;
        }
        socket.destroy();
    });

    server.on('clientError', function (error, socket) {
        if (socket && socket.writable) {
            socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
            return;
        }
        if (socket && typeof socket.destroy === 'function') {
            socket.destroy();
        }
    });

    server.on('error', function (error) {
        console.error('Dev server error:', error && error.message ? error.message : error);
    });

    server.listen(options.port, function (error) {
        if (!options.noLog) {
            console.log('Application started on http://localhost:' + options.port);
            console.log('Proxying ' + proxyPrefix + ' to ' + proxyTarget);
            console.log('Use AMBARI_PROXY_TARGET / AMBARI_PROXY_PREFIX to override defaults');
        }
        callback(error, options);
    });

    return server;
}

module.exports.startServer = startServer;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

# 三、使用示例与常见联调姿势

# 1、环境变量覆盖(多环境切换)

    export AMBARI_PROXY_TARGET="http://dev1.test.com:8080"
    export AMBARI_PROXY_PREFIX="/api"
    yarn start
    
    1
    2
    3
    AMBARI_PROXY_TARGET="http://dev1.test.com:8080" \
    AMBARI_PROXY_PREFIX="/api" \
    yarn start
    
    1
    2
    3
    // Make sure to add code blocks to your code group

    经验值

    AMBARI_PROXY_PREFIX 保持 /api,前端 Ember 侧基本不需要改任何路径,代理命中面最大。

    # 2、代理是否生效:两步验证

    1. 浏览器打开:http://localhost:3333

    2. 控制台 Network 看请求:

      • 访问 /api/v1/clusters 等接口时,请求仍然打到 localhost:3333(前端视角)
      • 后端真实流量被转发到 AMBARI_PROXY_TARGET

    想更直观一点

    看启动日志里这一行:

    • Proxying /api to http://dev1.test.com:8080

    这行能输出,通常说明配置命中、服务启动正常。

    # 四、重新启动即可生效

    注意

    修改完 brunch-server.js 后,重新 start 就好:

    • 停掉当前进程
    • 再执行一次 yarn start(或你的启动命令)
    #Ambari#Ambari-Web#前端二开#Brunch#http-proxy#WebSocket#Express#Node.js#Yarn#WebStorm
    基于 Brunch Dev Server 反向代理连接生产环境

    ← 基于 Brunch Dev Server 反向代理连接生产环境

    最近更新
    01
    优惠政策
    03-02
    02
    更新日志 · v2.2.3 2026/03
    03-01
    03
    基于 Brunch Dev Server 反向代理连接生产环境
    02-25
    更多文章>
    Theme by Vdoing | Copyright © 2017-2026 JaneTTR | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式