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

深度解析nodejs中间件

时间:2023-04-03 18:30:55 Node.js

nodejs的出现给前端行业带来了无限的可能,很多原本只负责客户端开发的同学也逐渐开始接触和使用服务端技术。虽然nodejs带来了很多好处,但它也有自己的局限性。与那些传统的老牌编程语言如JAVA相比,PHP.nodejs无法成为它们的替代品,在可预见的未来,这些老牌编程语言的地位是难以撼动的。目前nodejs主要有以下应用场景。前端工程化,如rollup,webpack在工程化nodejs中层客户端集成的方向探索nodejs,如electron市面上一些不太复杂的应用选择nodejs作为后端编程语言本文主要讲一些nodejs作为中间层的做法,见下图。在传统的开发模式中,浏览器直接与服务器层通信。增加中间层意味着在浏览器层和服务器层之间增加了一个额外的层。原来客户端直接向服务器发送请求,服务器层收到请求后将结果返回给浏览器。现在浏览器向节点层发送请求,节点层经过一轮处理后,再向服务器层发送请求。服务器层处理返回响应结果给节点层,节点层最终返回数据给浏览器。因为node层的出现,server层可以只专注于业务本身,而不管前端对领域的特殊要求。节点层可以从服务器层获取数据,然后通过计算和集成将数据转换成满足前端UI要求的数据格式。另外,如果整个应用采用微服务架构,那么服务器层会有很多服务器管理各个业务模块。该层很好地适应了微服务架构。它可以向多个服务器发起请求,从不同的模块获取数据,然后将它们整合转换后发送给前端。下面将重点介绍nodejs作为中间层的一些实践。nodejs进阶视频讲解如何进入学习代理转发代理转发在实践中有很多广泛的应用。浏览器首先向节点服务器发送请求。节点服务器收到请求后,可以对请求做一些处理,比如改变原来的路径,以及请求头信息的改变,然后将修改后的请求发送到远程真实服务器。远程服务器计算响应结果返回给节点服务器。节点服务器仍然可以选择性地处理响应,然后将其返回给浏览器。代理转发可以解决前端日常开发中经常遇到的跨域问题,同时也屏蔽了远程真实服务器的细节,让浏览器只与节点服务器通信。下面是一个简单的练习。const表示=require('express');const{createProxyMiddleware}=require('http-proxy-middleware');constapp=express();//创建应用app.use("/api",createProxyMiddleware(//设置代理转发{target:'http://www.xxx.com',//随便写一个地址的例子changeOrigin: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函数修改访问Path。最终的访问路径变成了http://www.xxx.com/server/api/list,然后会向这个路径发起请求,response返回给浏览器。上面介绍的接口聚合转发在实践中很少单独使用。如果只是为了转发数据,还不如直接用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表示聚合两条数据的自定义路由。如果聚合数据的需求比较多,这个逻辑应该单独封装到路由模块中进行管理,写在代理转发的前面。这样就保证了需要转发的接口交给转发的逻辑处理,需要个性化数据处理的接口单独写。路由操作数据。用于boost的数据缓存缓存对系统性能和减轻数据库压力起着微不足道的作用。常用的缓存软件是redis,可以理解为一个数据库,数据存放在内存中。由于数据存储在内存中,读写速度非常快,可以极快的响应用户的请求。在节点层部署redis来管理缓存数据,可以提升整体应用性能。但是不建议将所有数据都存储在redis中,只有那些不经常变化的数据才应该设置为缓存。比如商品信息数据,浏览器发起一个商品的请求,想要查看商品的详细信息。请求第一次到达node层,此时redis是空的。然后节点开始请求server层获取响应结果,然后将响应结果返回给浏览器服务器之前,以请求的访问路径作为key值,响应结果存储在redis中为价值。这样,后面再发送同样的请求时,先检查redis是否缓存了请求的数据。如果有缓存,就直接返回数据,如果没有缓存,再去server层,再走一遍上面的流程。Redis还可以设置过期时间和清除缓存数据,可以根据具体业务进行操作。简单的做法如下。constexpress=require('express');constapp=express();//创建应用程序//伪代码app.use("*",(req,res,next)=>{constpath=req.originalUrl;//获取访问路径if(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在redis中找到对应的值,然后加1。如果同一个人多次重复访问,该值会在短时间内增加到一个很大的数时间。我们每次都可以判断这个数字是否超过设定的预期标准。如果超过,则拒绝本次Request。简单的做法如下。constexpress=require('express');constapp=express();//创建应用程序//伪代码app.use("*",(req,res,next)=>{constip=req.ip;让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();//release}})cacheData(app)//伪代码。缓存接口数据聚合(app);//伪代码,封装接口聚合的逻辑proxyHanlder(app);//伪代码,封装代理转发的逻辑app.use("*",(req,res)=>{res.send("helloworld");})app.listen(3000);在应用程序前面设置一层限流中间件,每次访问过来判断终端是否已经缓存。第一次访问一定不要缓存,所以把当前ip对应的值设置为0,加上5秒的过期时间。同样在下一次用户再次访问时,该值会加1。最终效果是5秒内调用接口超过20次,拒绝访问。日志操作系统没有日志,相当于没有眼睛的人。日志可以帮助我们找出在线系统中的错误,分析和定位错误。此外,还可以通过日志数据进行统计计算,得出一定的结论和趋势。node层可以承担管理日志的功能。以接口访问日志为例。在系统中创建一个新的日志文件夹。每次有接入请求,首先分析请求的路径、当前接入时间、携带的参数和终端数据。信息。然后在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',`${accesstime}-${accesspathandparameters}:${list}`);//将数据存入日志文件res.send(list);//返回结果给客户端})最后,中间层还可以做很多其他的事情,比如监控,认证和服务端渲染(ssr)。这部分由于内容较多,可分章进行。网上也有很多关于如何实践的文章,可以搜索参考学习。其实上面提到的所有功能都可以通过其他编程语言来实现,这也成为了很多人的疑问。有必要向架构添加额外的关注层。加入nodejs中间层对于前端同学来说绝对是个福音。因为它可以让前端承担更多的任务,增加前端的业务比重。另外,后端以后只需要关注自己的业务,前端继续做自己擅长的事情,可以整体提高开发效率。但从宏观上看,在架构上多加一层,势必会造成整个应用性能的损失,测试级别会增加运维成本。目前,前后端分离已经成为主流的开发模式。很多类型的应用都需要seo的支持和首屏的加载速度,所以服务端渲染是必不可少的。目前大多数前端项目使用的是React或vue框架开发,如果用nodejs来承担服务端渲染的任务,那么就可以保证一套代码既可以做客户端渲染,也可以做服务端渲染,而这些工作可以由前端独立完成终端程序员。服务端渲染技术非常重要,后面会单独一节讲解。综上所述,nodejs作为中间层最有价值的功能就是服务端渲染和接口数据聚合。如果企业应用数量少,业务简单,业务规模不大,不建议加中间层,把简单的事情搞复杂了。