Http请求转发服务器实现
需求场景
云服务器通过VPN连接了现场的n台工控机,每台工控机上都在跑web程序,现在我想通过公网直接访问工控机上的web服务,给客户查看现场的具体运行情况,而不是让客户再装一个VPN,简化操作。
现在我们有以下几种方案可以实现:
1.使用代理,将每台工控机的web服务端口代理到云服务器上的一个空闲端口,这种方式的优点是不需要额外写代码实现,只需要通过配置代理即可,缺点是云服务器上端口占用过多,每打开现场的网址url上面都会带一个端口号。
2.使用Http请求转发服务器,将现场和他对应的VPN的ip地址映射起来存到数据库里,前端先请求服务器本地IP地址(127.0.0.1),当通过主界面跳转到某个现场时,通过数据库查询现场VPN的IP地址,然后将之后的请求都转发到对应的VPN地址,即可获取对应现场的数据
具体实现
使用Python实现请求转发服务器
-
使用Python实现请求转发服务器非常简单,用flask库即可轻松实现
pythonimport queue import threading import requests from flask import Flask, request, jsonify from log import log # 初始化Flask应用 app = Flask(__name__, static_folder="dist") # 捕获所有请求并转发 @app.route("/", defaults={"path": ""}, methods=["GET", "POST", "PUT", "DELETE"]) @app.route("/<path:path>", methods=["GET", "POST", "PUT", "DELETE"]) def catch_all(path): # 构建转发的目标 URL,我这里将请求都转发到10.8.0.126服务器上 forward_url = f"http://10.8.0.126/{path}" # 转发请求,并传递请求头、数据以及文件(如果有) headers = {key: value for key, value in request.headers if key != "Host"} # 获取请求的数据,根据请求方法选择适当的数据处理方式 data = request.get_json() if request.is_json else request.form or request.data json_data = data or {} try: response = requests.request( method=request.method, url=forward_url, headers=headers, json=json_data, files=request.files, params=request.args, ) except requests.RequestException as e: return str(e), 500 # 返回转发请求的响应 return response.content, response.status_code, response.headers.items() if __name__ == "__main__": app.run(debug=False, host="0.0.0.0", port=9095)
-
安装gunicorn库,部署转发服务程序(普通运行只供调试使用,不适用于生产环境)
bashpip install gunicorn
-
运行转发服务程序
bashgunicorn -b 0.0.0.0 -w 4 main:app
使用C++实现请求转发服务器
在用Python完成请求转发服务器的开发后,方案却被领导否决了,因为后台程序都是用C++写的,用Python写不好融合,然后我继续开始研究C++服务端的方案,先采用cpp-httplib库实现。
-
代码实现,这里捕获所有请求使用了正则表达式,刚开始因为对这个库和正则不熟悉,卡了较长时间。
cpp#include <iostream> #include <httplib.h> #include <string> // 定义一个简单的路由处理器 void catch_all(const httplib::Request& req, httplib::Response& res) { // 构建转发的目标 URL std::string path = req.path; std::cout << "path: " << path << std::endl; std::cout << "method: " << req.method << std::endl; std::string forward_url = "http://10.8.0.126/" + path; // 转发请求,并传递请求头、数据以及文件(如果有) httplib::Headers headers; headers = req.headers; headers.erase("Host"); // 获取请求的数据,根据请求方法选择适当的数据处理方式 std::string data; data = req.body; // 使用 httplib 客户端进行转发 httplib::Client client("10.10.112.139", 9092); httplib::Result result; if (req.method == "GET") { result = client.Get(path, headers); } else if (req.method == "POST") { result = client.Post(path, headers, data, "application/json"); std::cout << result->body << std::endl; } else if (req.method == "PUT") { result = client.Put(path, headers, data, "application/json"); } else if (req.method == "DELETE") { result = client.Delete(path, headers); } else { res.set_content("Unsupported method", "text/plain"); res.status = 405; return; } if (result && result->status == 200) { res.set_content(result->body, result->get_header_value("Content-Type")); res.status = result->status; for (const auto& header : result->headers) { res.headers.emplace(header.first, header.second); } } else { res.set_content("Forward request failed", "text/plain"); res.status = 500; } } int main() { httplib::Server svr; // 捕获所有请求并转发 svr.Get(R"(/.*)", &catch_all); svr.Post(R"(/.*)", &catch_all); auto ret = svr.set_mount_point("/", "/home/narada/ems/www"); svr.listen("0.0.0.0", 8080); return 0; }