当前位置: 首页 > 后端技术 > Node.js

深度解析nodejs中间件_0

时间:2023-04-04 01:33:57 Node.js

nodejs的出现给前端行业带来了无限的可能,很多原本只负责客户端开发的同学也逐渐开始接触和使用服务端技术。虽然nodejs带来了很多好处,但它也有自己的局限性。与那些传统的老牌编程语言如JAVA相比,PHP.nodejs无法成为它们的替代品,在可预见的未来,这些老牌编程语言的地位是难以撼动的。目前nodejs主要有以下应用场景。前端工程化,如rollup,webpack在工程化nodejs中层客户端集成的方向探索nodejs,如electron市面上一些不太复杂的应用选择nodejs作为后端编程语言本文主要讲一些nodejs作为中间层的做法,见下图。在传统的开发模式中,浏览器直接与服务器层通信。增加中间层意味着在浏览器层和服务器层之间增加了一个额外的层。原来客户端直接向服务器发送请求,服务器层收到请求后将结果返回给浏览器。现在浏览器向节点层发送请求,节点层经过一轮处理后,再向服务器层发送请求。服务器层处理返回响应结果给节点层,节点层最终返回数据给浏览器。因为node层的出现,server层可以只专注于业务本身,而不管前端对领域的特殊要求。节点层可以从服务器层获取数据,然后通过计算和集成将数据转换成满足前端UI要求的数据格式。另外,如果整个应用采用微服务架构,那么服务器层会有很多服务器管理各个业务模块。该层很好地适应了微服务架构。可以向多个服务器发起请求,获取不同模块的数据,然后进行整合转换,发送给前端。下面将重点介绍nodejs作为中间层的一些实践。代理转发agentforwarding在实践中有很多广泛的应用。浏览器首先向节点服务器发送请求。节点服务器收到请求后,可以对请求做一些处理,比如改变原来的路径,改变请求头中的信息,然后修改最终请求发送到远程真实服务器。远程服务器计算响应结果返回给节点服务器。节点服务器仍然可以选择性地处理响应,然后将其返回给浏览器。代理转发可以解决前端日常开发中经常遇到的问题。此外,它还屏蔽了远程真实服务器的细节,使浏览器只与节点服务器通信。下面是一个简单的练习。constexpress=require('表达');const{createProxyMiddleware}=require('http-proxy-middleware');constapp=express();//创建应用app.use("/api",createProxyMiddleware(//设置代理转发{target:'http://www.xxx.com',//地址随机变化示例Origin:true,pathRewrite:function(path){returnpath.replace('/api','/server/api');}}));app.use("*",(req,res)=>{//所有不以'/api'开头的路由返回"helloworld"res.send("helloworld");})app.listen(3000);http-proxy-middleware是第三方依赖包,设置代理转发非常方便,需要通过npm安装。如果当前访问路径以/api开头,那么请求就会被http-proxy-middleware拦截。观察http-proxy-middleware中配置的参数。target表示远程真实服务器的地址。changeOrigin设置为true,表示请求转发到目标地址。pathRewrite是对请求路径进行处理,将/api转换为/server/api。上述案例的意义显而易见。如果当前浏览器访问http://localhost:3000/api/list。因为这个路径以/api开头,所以会被拦截,从而触发pathRewrite函数修改访问路径。最后,访问路径变成http://www.xxx.com/server/api/list,然后会向这个路径发起请求,响应会返回给浏览器。参考nodejs进阶视频讲解:进入学习接口聚合上面介绍的接口转发在实践中很少单独使用。如果只是为了转发数据,还不如直接用nginx配置,转发就搞定了。如果接口聚合和接口转发都需要如果是,还是优先从代码层面解决。接口聚合是什么意思?假设公司现在有两个销售系统,一个是线上电商平台销售,一个是线下实体店。他们分别属于不同的团队,维护着不同的数据系统。如果当前请求只是查询电商平台某商品的信息,只需要将接口转发给电商平台系统即可。同理,如果你线下只查询实体店某一天的销售业绩,可以直接将请求转发到线下数据系统进行查询,然后返回响应数据。上面介绍的插件http-proxy-middleware支持配置多个代理路径,具体可以查询文档。现在有一个需求,目标是查询某商品本周线上线下销售数据对比。那么此时节点层需要向两个远程服务器发送请求,分别获取线上销售数据和线下销售数据。部分数据聚合后返回给前端。简单的做法如下。constexpress=require('快递');const{createProxyMiddleware}=require('http-proxy-middleware');constapp=express();//创建应用程序//伪代码app.get("/getSaleInfo",async(req,res)=>{constonline_data=awaitgetOnline();//获取在线数据constoffline_data=awaitgetOffline();//获取离线数据res.send(dataHanlder(online_data,offline_data));//数据处理后返回给前端})proxyHanlder(app);//伪代码,封装代理转发逻辑app.use("*",(req,res)=>{res.send("helloworld");})app.listen(3000);/getSaleInfo表示聚合两个数据的自定义路由,如果有很多需要聚合数据,这个块逻辑要单独封装到路由模块中管理,写在代理转发的前面。这样就保证了需要转发的接口交给转发逻辑处理,需要个性化数据处理的接口单独写。路由操作数据。数据缓存缓存对对提高系统性能,减轻数据库压力的作用微乎其微。常用的缓存软件是redis,可以理解为一个数据库,数据存放在内存中。由于数据存储在内存中,读写速度非常快,可以非常快速地响应用户请求。在节点层部署redis来管理缓存数据,可以提升整体应用性能。但是不建议将所有数据都存储在redis中,只有那些不经常变化的数据才应该设置为缓存。比如商品信息Data,浏览器发起一个商品的请求,想要查看商品的详细信息。请求第一次到达node层,此时redis是空的。然后节点开始请求server层获取响应结果,然后将响应结果返回给浏览器,再将请求的访问路径作为key值,响应结果存储在redis中为价值。这样,后面发送同样的请求时,首先检查redis是否缓存了请求的数据,如果缓存了直接返回数据。如果没有缓存,就到server层再走一遍上面的流程。Redis还可以设置过期时间和清除缓存数据,可以根据具体业务进行操作。简单的做法如下。constexpress=require('express');constapp=express();//创建应用//伪代码app.use("*",(req,res,next)=>{constpath=req.originalUrl;//getaccesspathif(redisClient.getItem(path)){//检查redis中是否缓存了该接口的数据res.send(redisClient.getItem(path));//返回缓存数据}else{next();//不执行任何操作,直接释放}})aggregate(app);//伪代码,封装接口聚合的逻辑proxyHanlder(app);//伪代码,封装代理转发的逻辑app.use("*",(req,res)=>{res.send("helloworld");})app.listen(3000);接口限流节点作为中间层可以限制前端的无限制访问。比如一些恶意脚本循环访问界面,每秒访问几十次,增加了服务器的负载。Redis可以提供帮助帮助我们实现这个功能。用户第一次访问,解析出本次请求的ip地址,将ip作为key值,并将value设置为0存入redis。当用户第二次访问时,取出ip,在redisvalue中找到对应的ip地址,然后加1。如果同一个人重复多次访问,value会在a中增加到一个很大的数很短的时间。我们每次都可以拿到这个数来判断是否超过了设定的预期标准,超过了就拒绝这个请求。简单的做法如下。constexpress=require('快递');constapp=express();//创建应用程序//伪代码app.use("*",(req,res,next)=>{constip=req.ip;letnum=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();//release}})cacheData(app)//伪代码。缓存接口数据聚合(app);//伪代码,封装接口聚合的逻辑proxyHanlder(app);//伪代码,封装代理转发的逻辑app.use("*",(req,res)=>{res.send("helloworld");})app.listen(3000);在应用程序前面设置一层限流中间件,每次访问过来判断终端是否已经缓存。第一次访问一定不要缓存,所以将当前ip对应的值设置为0,加上过期时间为5秒。Next当同一用户再次访问时,该值会加1。最终效果是,如果5秒内调用该接口的次数超过20次,则拒绝访问。日志操作系统没有日志,相当于没有眼睛的人。日志可以帮助我们分析定位线上系统的错误。此外,还可以通过日志数据进行统计计算,得出一定的结论和趋势潜在的。节点层可以承担管理日志的功能。以接口访问日志为例。在系统中创建一个新的日志文件夹。每次有接入请求,首先解析请求的路径、当前接入时间、参数和终端数据信息。然后在log文件夹下创建一个txt文件存放当天的日志情况,将上面的数据和请求的响应结果组合成一条记录插入到txt文件中。继续上面的过程,将访问日志添加到txt文件中,供下次访问使用。和上面介绍的代理转发一样,插件http-proxy-middleware支持配置如何返回响应结果,那么在对应的事件函数hook中就可以同时获取到请求和响应,而这两条数据可以存储在日志中。在这里你还可以制定很多配置策略。可以选择每天一篇日志文本,如果流量大的话,也可以选择每小时一篇日志文本,根据实际情况而定。另外,随着时间的推移,日志文件夹的文件内容会越来越多。这就需要编写linux操作系统的定时任务来迁移备份这些日志数据。日志操作的简单做法如下//伪代码app.use("/getList",async(req,res)=>{constlist=awaitgetProductList();//获取商品数据const{accesstime,accesspath,parameters}=req;logger.log('info',`${访问时间}-${访问路径和参数}:${list}`);//将数据存入日志文件res.send(list);//返回结果给客户端})最后,中间层还可以做很多其他的事情,比如监控,认证和服务端渲染(ssr)。这部分由于内容较多,可分章进行。网上也有很多关于如何实践的文章,可以搜索参考学习。其实上面提到的所有功能都可以通过其他编程语言来实现,这也成为了很多人的心声。质疑是否有必要向架构添加额外的关注层。增加一个nodejs中间层对于前端同学来说绝对是个福音。因为它可以让前端承担更多的任务,增加前端业务的比重。另外从现在开始,后端只需要关注自己的业务,前端继续做自己擅长的事情,这样可以整体提升开发效率。但从宏观上看,在架构上多加一层,必然会造成应用整体性能的损失。在部署和测试层面,会增加运维成本。目前,前后端分离已经成为主流的开发模式。很多类型的应用都需要seo的支持和首屏的加载速度,所以服务端渲染是必不可少的。目前大部分前端项目都是用react或者vue框架开发,如果用nodejs来承担服务端渲染的任务,那么就可以保证一套代码既可以做客户端渲染,也可以做服务端渲染,而这些任务可以独立完成前端程序员。Server-sideRendering技术非常重要,后面会单独说明。综上所述,nodejs作为中间层最有价值的功能就是服务端渲染和接口数据聚合。如果企业应用数量少,业务简单,规模不大,不建议加中间层,把简单的事情搞复杂了。