乐趣区

关于node.js:深入剖析nodejs中间件

nodejs的呈现为前端行业带来了有限的可能性, 让很多原来只负责客户端开发的同学也缓缓开始接触和应用服务器端技术.

尽管 nodejs 带来了很多的益处, 然而它也存在本身的局限性. 和那些传统老牌的编程语言相比, 如 JAVA,PHP.nodejs 并不能成为它们的替代品, 而且在可预估的将来, 也很难撼动那些老牌编程语言的位置.

目前 nodejs 次要有以下几个利用场景.

  • 前端工程化, 比方 rollup,webpack 在工程化方向的摸索
  • nodejs中间层
  • 客户端集成nodejs, 比方electron
  • 市面上一些不太简单的利用抉择 nodejs 作为后端编程语言

本文次要讲一讲 nodejs 作为中间层的一些实际, 查看下图.

传统的的开发模式由浏览器间接和 Server 层间接通信, 中间层的退出意味着在浏览器和 Server 层之间额定增加了一层.

原来客户端间接向 Server 发送申请,Server层收到申请后通过计算解决将后果返回给浏览器.

现在浏览器将申请发送给 node 层,node 层 通过一轮解决后再向 Server 层 发动申请.Server 层 处理完毕将响应后果返回给 node 层,node 层 最初将数据返回给浏览器.

因为 node 层 的呈现,Server 层 能够只用关注业务自身, 而不用理睬前端对字段的特殊要求。

node 层 能够向 server 层获取数据, 再通过对数据的计算整合转换成合乎前端 UI 要求的数据格式. 另外整个利用如果采纳微服务架构, 那么 Server 层 会有很多台治理独自业务模块的服务器,node 层 就很好的适配了微服务的架构, 它能够向多台服务器发动申请获取到不同模块的数据再整合转化发送给前端.

上面着重介绍一下 nodejs 作为中间层的局部实际.

代理转发

代理转发在理论中有很多宽泛的利用. 浏览器首先将申请发送给 node 服务器, 申请收到后node 服务器 能够对申请做一些解决, 比方将原来的门路变换一下, 申请头的信息扭转一下, 再把批改后的申请发送给近程实在的服务器.

近程服务器计算出响应后果再返回给 node 服务器,node 服务器 依然能够对响应做选择性解决再分返回给浏览器.

代理转发能够解决前端日常开发中常常遇到的跨域问题, 另外它还屏蔽了近程实在服务器的细节, 让浏览器只与 node 服务器 通信. 上面是简略的实际.

const express = require('express');
const {createProxyMiddleware} = require('http-proxy-middleware');

const app = express();// 创立利用

app.use("/api",createProxyMiddleware( // 设置代理转发
  { 
     target: 'http://www.xxx.com', // 举例轻易写的地址
     changeOrigin: true,
     pathRewrite: function (path) {return path.replace('/api', '/server/api');
     }
  })
);

app.use("*",(req,res)=>{  // 不是以 '/api' 结尾的路由全副返回 "hello world"
  res.send("hello world");
})

app.listen(3000);

http-proxy-middleware是一个第三方依赖包, 能够十分不便设置代理转发, 须要通过 npm 装置.

如果以后拜访的门路是以 /api 结尾, 那么该申请就会被 http-proxy-middleware 拦挡. 察看 http-proxy-middleware 外面配置的参数.

  • target代表近程实在服务器的地址.
  • changeOrigin设置为 true, 示意将申请转发到target 地址上.
  • pathRewrite是对申请门路做一下解决, 将 /api 转换成/server/api.

下面的案例意思很显著, 如果以后浏览器拜访 http://localhost:3000/api/list. 因为这个门路以/api 结尾所以会被拦挡, 从而触发 pathRewrite 函数批改拜访门路. 最终拜访门路就变成了http://www.xxx.com/server/api/list, 而后就会向这个门路发动申请, 失去响应后再返回给浏览器,参考 nodejs 进阶视频解说:进入学习

接口聚合

下面介绍的接口转发在实践中很少会独自利用, 如果仅仅只是为了转发一下数据, 那还不如间接用 nginx 配置一下,转发就搞定了.

如果接口聚合和接口转发都须要, 那么从代码层面去解决还是优先思考的形式.

接口聚合是什么意思呢? 假如当初企业有两个销售体系, 一个是线上的电商平台销售, 另一个是线下实体店. 它们别离属于不同的团队经营, 保护着不同的数据系统.

如果以后申请只是想查问一下电商平台某款商品的信息, 只须要将接口转发给电商平台零碎即可. 同理如果仅仅只是查问线下实体店某一天的销售业绩, 能够间接把申请转发给线下数据系统查问, 再把响应数据返回. 下面介绍的插件 http-proxy-middleware 反对配置多个代理门路, 具体可查问文档.

当初有这么一个需要, 指标是查问本周某款商品在线上和线下销售数据的比照. 那么这个时候就须要 node 层 向两个近程服务器发送申请别离获取线上销售数据和线下销售数据, 将这两局部数据聚合解决后再返回给前端. 简略实际如下.

const express = require('express');
const {createProxyMiddleware} = require('http-proxy-middleware');

const app = express();// 创立利用

// 伪代码
app.get("/getSaleInfo",async (req,res)=>{const online_data =  await getOnline(); // 获取线上数据
   const offline_data = await getOffline(); // 获取线下数据
   res.send(dataHanlder(online_data,offline_data)); // 对数据处理后返回给前端
})

proxyHanlder(app);// 伪代码, 将代理转发的逻辑封装起来

app.use("*",(req,res)=>{res.send("hello world");
})

app.listen(3000);

/getSaleInfo代表着将两条数据聚合的自定义路由, 如果须要聚合数据的需要比拟多, 这块逻辑要独自封装到路由模块中治理, 并且要写在代理转发的后面.

这样就确保了须要转发的接口就交给转发的逻辑解决, 须要个性化解决数据的接口就独自编写路由操作数据.

数据缓存

缓存对于晋升零碎性能, 减小数据库压力起到了举足轻重的作用. 个别罕用的缓存软件是redis, 它能够被了解成数据存储在内存当中的数据库. 因为数据放在内存中, 读写速度十分快, 能极快的响应用户的申请.

node 层 部署 redis 治理缓存数据, 能够晋升整体利用性能. 但不是什么数据都倡议寄存在 redis 中, 只有那些不常常变动的数据应该设置成缓存.

比方商品的信息数据, 浏览器对某个商品发动申请, 想查看该商品的详情. 申请第一次达到 node 层,redis此时是空的. 那么 node 开始申请 server 层失去响应后果, 此时在将响应后果返回给浏览器之前, 将该次申请的拜访门路作为 key 值, 响应后果作为 value 存储到 redis 中. 这样之后再有雷同的申请发来时, 先查看 redis 有没有缓存该申请的数据, 如果缓存了间接将数据返回, 如果没有缓存再去申请 server 层, 把上述流程再走一遍.

redis还能够对缓存数据设置过期工夫和革除, 能够依据具体的业务操作. 简略实际如下.

const express = require('express');

const app = express();// 创立利用

// 伪代码
app.use("*",(req,res,next)=>{
   const path = req.originalUrl; // 获取拜访门路
   if(redisClient.getItem(path)){ // 查看 redis 中有没有缓存该条接口的数据
        res.send(redisClient.getItem(path)); // 返回缓存数据
   }else{next(); // 不执行任何操作, 间接放行        
   }
})


aggregate(app); // 伪代码, 将接口聚合的逻辑封装起来

proxyHanlder(app);// 伪代码, 将代理转发的逻辑封装起来

app.use("*",(req,res)=>{res.send("hello world");
})

app.listen(3000);

接口限流

node做中间层能够对前端无节制的拜访做限度. 比方有些歹意的脚本循环拜访接口, 一秒钟拜访几十次增大了服务器的负载.

redis能够帮忙咱们实现这一性能. 用户第一次拜访, 解析出本次申请的 ip 地址, 将 ip 作为 key 值,value置为 0 存到 redis 中.

用户第二次拜访, 取出 ip 找到 redis 中对应的 value, 而后自增1. 如果是雷同的人反复大量拜访,value 在短期内就自增到了很大的数字, 咱们能够每次获取这个数字判端是否超过了设定的预期规范, 超过则回绝本次申请. 简略实际如下.

const express = require('express');

const app = express();// 创立利用

// 伪代码
app.use("*",(req,res,next)=>{

  const ip = req.ip;

  let num = 0;

  if(redisClient.getItem(ip)){ // 是否缓存了以后的 ip 字段
    num = redisClient.incr(ip); // 每拜访一下, 计数加 1
  }else{redisClient.setItem(ip,0);
    redisClient.setExpireTime(5); // 设置过期工夫为 5 秒,5 秒后再获取该 ip 为空
  }

  if(num > 20){res.send("非法拜访");
  }else{next();// 放行
  }

})

cacheData(app)// 伪代码. 缓存接口数据

aggregate(app); // 伪代码, 将接口聚合的逻辑封装起来

proxyHanlder(app);// 伪代码, 将代理转发的逻辑封装起来

app.use("*",(req,res)=>{res.send("hello world");
})

app.listen(3000);

在利用的后面设置一层限流中间件, 每次拜访降临先判端是否缓存过. 第一次拜访必定没有缓存, 就将以后 ip 对应的值设置为 0 并增加过期工夫为 5 秒钟. 下一次雷同的用户再拜访时就会将 value 自增1.

最初的成果就达到了 5 秒内调用接口的次数超过 20 次便回绝拜访.

日志操作

零碎没有日志, 相当于人没有双眼. 日志能够帮忙咱们发现剖析定位线上零碎呈现的谬误. 另外通过日志数据也能够进行统计计算得出某些论断和趋势.

node层可能承当起治理日志的性能, 以接口拜访日志为例. 在零碎中新建一个日志文件夹, 每次有申请拜访时, 首先解析申请的门路、以后的拜访工夫以及携带的参数和终端数据信息. 而后在日志文件夹创立一个 txt 文件寄存当天日志状况, 将上述数据和该申请的响应后果组合成一条记录插入 txt 文件中. 下一次拜访持续走下面流程往 txt 文件增加拜访日志. 像下面介绍的代理转发, 插件 http-proxy-middleware 反对配置如何返回响应后果, 那么在相应的事件函数钩子里就能够同时失去申请和响应, 有了这两块数据就能够寄存到日志中.

这里还能制订很多的配置策略. 能够抉择一天一个日志文本, 如果访问量微小也能够抉择一个小时一个日志文本, 根据理论状况而定.

另外随着工夫的缩短, 日志文件夹的文件内容会越来越多. 这就须要编写 linux 操作系统定时工作来迁徙和备份这些日志数据.

日志操作简略实际如下.

// 伪代码
app.use("/getList",async (req,res)=>{const list = await getProductList(); // 获取商品数据
  const {拜访工夫, 拜访门路, 参数} = req;
  logger.log('info',`${拜访工夫}-${拜访门路和参数}:${list}`);// 将数据存储到日志文件中 
  res.send(list);// 将后果返回给客户端
})

结尾

中间层另外还能够做很多其余事件, 比方监控、鉴权和服务器端渲染(ssr). 这部分因为内容比拟多能够独自成章, 网络上也有大量如何实际的文章, 可搜寻查阅学习.

其实下面所谈到的所有性能其余编程语言都能够做到, 这也成为了很多人质疑是否须要在架构上额定再加一层的顾虑.

增加 nodejs 中间层, 对于前端同学来说必定是好消息. 因为它能让前端承当更多的工作工作, 让前端的业务比重变大. 另外后端从此只须要关注本身业务, 前端持续干着本人善于的事, 从整体上是能晋升开发效率.

但从宏观角度上看, 架构额定减少一层势必会造成整个利用性能上的损耗, 另外在部署, 测试层面都会增大运维老本.

当下前后端拆散曾经成为了支流的开发模式, 很多类型的利用须要 seo 的反对以及首屏加载速度, 因而服务器端渲染不可或缺. 前端我的项目目前大多采纳 reactvue框架开发, 如果用 nodejs 承当服务器端渲染的工作, 那么能够确保一套代码既能够做客户端渲染也能反对服务器端渲染, 而这些工作都能够让前端程序员独立来实现. 服务器端渲染技术十分重要, 前面会开一个大节独自解说.

综上来看,nodejs做中间层最有价值的性能是服务器端渲染和接口数据聚合. 如果企业应用数量较少业务简略还没有规模化, 不倡议增加中间层, 那样反而让简略的事件变得复杂.

退出移动版