nodejs 中间件 反向代理 接口转发
背景
随着后端业务系统的增加,纵向需求不断扩展,一个业务系统已经无法满足需求了,衍生出多个业务系统,对外暴露的ip、端口就可能有多个,此时不方便外部接口调用,有些特殊行业客户出于安全性考虑不发提供多个对外端口,对外只能提供一个IP一个端口。
这时中间件就产生了!
概念
中间件是一种独立的系统软件服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。
一个统一过滤请求的中间层,这就是中间件。
用途
中间件常见的用途有:
- IP过滤
- 防爬虫
- 解析请求
- 合并接口
- 合并端口
- cookie处理
- 参数校验
- 权限校验
- 异常处理
- 负载均衡
- 反向代理
这样业务系统就可以关注点集中在业务层,隔离这些基础设施,让开发者专注于业务以提高开发效率。
技术选型
- 高并发
- 传输快
- 非阻塞(NIO)
常用的有nodejs、netty、go
案例demo
这里以nodejs为例,实现接口反向代理和合并端口功能
功能描述:
有两个业务系统:财务系统(financeSys)、用户系统(userSys),
分别部署到:
财务系统(financeSys):http://192.168.0.2/find
用户系统(userSys):http://192.168.0.3/login
对外暴露的接口分别是:
http://www.xxx.com/financeSys/find
http://www.xxx.com/userSys/login
对外暴露的接口格式为:http://www.xxx.com/系统名/业务URL,中间件系统根据请求地址的系统名来转发到对应的业务系统中
这时在www.xxx.com这个服务器上部署一个中间件,
收到http://www.xxx.com/financeSys/find的请求时转发到财务系统(financeSys)http://192.168.0.2/find
收到http://www.xxx.com/userSys/login的请求时转发到用户系统(userSys)http://192.168.0.3/login
架构图
图片.pngNodejs项目分析
- 使用的第三方框架
koa: 网络框架,用来搭建服务
koa2-proxy-middleware:反向代理处理框架
log4js:日志框架 - 配置转发的关系的映射文件
{
"RequestLog": true, // 是否打印请求日志
"ResponseLog": true, // 是否打印响应日志
"ServerPort": 80, // 中间件服务端口,即对外暴露的接口端口
"financeSys": "http://192.168.0.2", // 财务系统(financeSys)的转发地址
"userSys": "http://192.168.0.3" // 用户系统(userSys)的转发地址
}
- 根据url中的系统名找到映射文件中配置的业务系统
如:/financeSys/find -> http://192.168.0.2
router: function(req) {
const path = req.url;
let pathArray = path.split("/");
let projectContext = pathArray[1];
// let suffix = path.substring(projectContext.length + 1, path.length);
let serverUrl = config[projectContext];
if (serverUrl != undefined && serverUrl != null) { // 配置文件已经配置转发路由
let proxyServer = serverUrl;
return proxyServer;
} else { // 没有配置路由转发
if (config.RequestLog) {
logger.info("No Proxy Server!! path:" + path);
}
return {
protocol: req.protocol,
host: req.host,
port: config.ServerPort
};
}
}
- 重写业务url
如:/financeSys/find -> /find
pathRewrite: function(path, req) {
let pathArray = path.split("/");
let projectContext = pathArray[1];
let suffix = path.substring(projectContext.length + 1, path.length);
return suffix;
}
- 打印请求/响应日志
onProxyReq: async function(proxyReq, req, res) {
// 禁用缓存
proxyReq.setHeader('Cache-Control', 'no-cache');
// 标示接口赖在中间件
proxyReq.setHeader('Interface-From', 'Middleware');
if (config.RequestLog) {
logger.info("---------------------------- Request ----------------------------");
logger.info('URL:' + req.url);
logger.info('Method:' + req.method);
logger.info('Headers:' + JSON.stringify(req.headers));
const responseBodyStr = await getBody(req)
logger.info("Body:" + responseBodyStr);
}
},
onProxyRes: async function(proxyRes, req, res) {
if (config.ResponseLog) {
logger.info("---------------------------- Response ----------------------------");
logger.info("URL:" + req.url);
const responseBodyStr = await getBody(proxyRes)
logger.info("Body:" + responseBodyStr);
}
}
- 过滤某些请求不转发
app.use(async (ctx, next) => {
let url = ctx.originalUrl;
if (url != '/favicon.ico') {
await next();
} else {
let resStr = JSON.stringify({})
ctx.response.body = resStr;
}
});
项目运行打包
- 安装nodejs,可以参考官网教程或菜鸟教程(https://www.runoob.com/nodejs/nodejs-tutorial.html)
- 下载源码解压
- 安装依赖,进入项目根目录,执行命令npm install
npm install
- 本地调试,可以使用Visual Studio/Visual Studio Code/WebStorm等ide打开,运行命令npm run start
npm run start
- 打包,运行命令npm run build,会在dist文件夹生成一个index.js文件
- 运行,在安装有nodejs环境的机器上运行打包生成的index.js文件,运行命令node index.js
node index.js
项目源码下载
项目源码:https://codechina.csdn.net/it1/project-middleware.git