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

nodejs作为中间层的实践《详细介绍》

时间:2023-04-03 19:23:37 Node.js

nodejs的出现给前端行业带来了无限可能,很多原本只负责客户端开发的同学也逐渐开始接触和使用服务器端技术。虽然nodejs带来了很多好处,但是它也有自己的局限性。与那些传统的编程语言相比,比如JAVA,PHP.nodejs无法成为它们的替代品,而且在可预见的未来,也很难撼动那些老牌编程语言的地位。目前nodejs主要有以下应用场景。前端工程,比如rollup,webpack在探索nodejs中层客户端集成nodejs的工程方向,比如市面上一些不太复杂的应用选择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',//写一个地址的例子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函数修改访问路径。最终的访问路径是变成了http://www.xxx.com/server/api/list,然后就会向这个路径发起请求,得到响应后返回给浏览器。有的时候服务端在修复接口数据可靠性的时候返回给我们的数据可能不是前端想要的结构。所有使用的显示数据均由后端通过异步接口(AJAX/JSONP)提供。前端只是展示,后端往往提供后端数据逻辑。这些数据逻辑需要处理。比如我在开发另外一个功能的时候,有时会遇到这样的问题:服务器返回的数据格式,前端没有处理,如果服务器返回的某个字段为null或者数据不正确,就会报错服务器返回的结构太深。编写这样的代码来检查数据结构是否实际返回正确的东西,而不是null或未定义:if(params.items&¶ms.items.type&&...){//todo}对于这种情况,实际上,我们前端不应该反复验证数据的格式,这不应该是浏览器端js需要做的事情。我们可以在中间层做接口转发,在转发过程中做数据处理。而不是担心数据返回:router.get('/buyer/product/detail',(req,res,next)=>{httpRequest.get('/buyer/product/detail',(data)=>{//todoprocessingdatares.send(data);})})页面性能优化和SEO有时候我们在做单页应用的时候,经常会遇到首屏加载性能的问题。如果此时我们连接中间层的nodejs,那么我们就可以把渲染第一屏的任务交给nodejs,而第二屏的渲染依然沿用之前的浏览器渲染。(前端换页,浏览器端渲染,直接输入URL,服务端渲染)服务端渲染拼接页面,直接输出html字符串,可以大大增加首屏渲染时间,减少用户等待时间.这种形式应用最为广泛,比如Vue的服务端渲染,里面也有相关介绍。其次,它也是处理单页SEO优化的好方法。由于目前百度等搜索引擎不支持ajax,如果想获得爬虫的支持,服务端渲染也是一种解决方案。接口聚合上面介绍的接口转发在实践中很少单独使用。如果只是为了转发数据,还不如直接用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.使用("*",(req,res)=>{res.send("helloworld");})app.listen(3000);复制代码/getSaleInfo表示聚合两个数据的自定义路由。如果聚合数据的需求比较多,这个逻辑需要单独封装到路由模块中进行管理,而且一定要写在代理转发的前面。通过这种方式,可以肯定需要转发的接口保证转发到转发的逻辑处理,需要个性化处理数据的接口单独写路由操作数据。数据缓存在提高系统性能和减轻数据库压力方面起着微不足道的作用。常用的缓存软件是redis,可以理解为一个数据库,数据存放在内存中。由于数据存储在内存中,读写速度非常快,可以非常快速地响应用户的请求。在节点层部署redis来管理缓存数据,可以提高整体应用性能。但并不是所有的数据都推荐存储在redis中,只有那些不经常变化的数据才应该设置为缓存。比如一个商品的信息数据,浏览器发起一个商品的请求,想要查看商品的详细信息。Request第一次到达node层时,此时redis是空的。然后node开始向server层请求得到响应结果。此时,在将响应结果返回给浏览器之前,将请求的访问路径作为键值,将响应结果作为值存储在redis中。这样,后面再发送同样的请求时,先检查redis是否缓存了请求的数据。如果有缓存,则直接返回数据。如果没有缓存,那就去server层,走上面的流程。再一次。Redis还可以根据具体的业务操作,设置缓存数据的过期时间和清除。简单的做法如下。constexpress=require('express');constapp=express();//创建应用程序//伪代码app.use("*",(req,res,next)=>{constpath=req.originalUrl;//获取访问路径if(redisClient.getItem(path)){//检查item是否缓存在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。如果同一个人重复大量访问,值会在短时间内增长到一个很大的数时间,而我们每次都可以得到这个数字判断是否超过了设定的预期标准,如果超过了,请求就会被拒绝。简单的做法如下。constexpress=require('express');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秒,之后获取ip5秒为空}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次,则拒绝访问。淘宝常见需求解决方案需求:在淘宝,单日PV4亿,页面数据来自各种接口。出请求影响不小,尤其是无线端方案:在NodeJS端使用Bigpiper技术,合并请求,减轻负担,批量输出,不影响体验。同时可以将大接口拆分成独立的小接口,用于并发请求。Serial=>Parallel,大大缩短了请求时间。日志操作系统没有日志,相当于没有眼睛的人。日志可以帮助我们发现、分析和定位在线系统中的错误。此外,日志数据还可以用来进行统计计算,得出一定的结论和趋势。节点层可以承担日志管理功能,以接口访问日志为例。在系统中新建一个日志文件夹,每次有访问请求,首先分析请求路径、当前访问时间、携带的参数和终端数据信息。然后在log文件夹下创建一个txt文件存放当天的日志,将上面的数据和请求的响应结果组合成一条记录插入到txt文件中。下次访问时,继续上述流程,将访问日志添加到txt文件中。和上面介绍的代理转发一样,插件http-proxy-middleware支持配置如何返回响应结果,那么在对应的事件函数hook中就可以同时获取请求和响应,而这两条数据可以存储在日志中。许多配置策略可以在这里制定。选择每天一篇日志文本,如果访问量大,则选择每小时一篇日志文本,视实际情况而定。另外,随着时间的推移,日志文件夹中的文件内容会越来越多。这就需要编写linux操作系统调度任务来迁移和备份这些日志数据。日志操作的简单做法如下//伪代码app.use("/getList",async(req,res)=>{constlist=awaitgetProductList();//获取商品数据const{访问时间,accesspath,parameters}=req;logger.log('info',`${accesstime}-${accesspathandparameters}:${list}`);//将数据存储到日志文件res.send(list);//返回结果给客户端})endofcopycode中间层还可以做很多其他的事情,比如监控,认证和服务端渲染(ssr)。这部分由于很多内容可以单独写,网上也有很多关于如何实践的文章,可以搜索一下参考学习。其实上面提到的所有功能都可以通过其他编程语言来实现,这也成为了很多人的一个难题。增加另一层顾虑。加入nodejs中间层对于前端同学来说绝对是个福音。因为它可以让前端承担更多的工作任务,增加前端业务的比重。另外,后端以后只需要专注于自己即可。业务,前端继续做自己擅长的事情,可以整体提升开发效率。但从宏观上看,在架构上多加一层,势必会造成整个应用性能的损失。此外,它将增加部署和测试级别。运维成本大。目前的前后端分离它已经成为主流的开发模式。很多类型的应用都需要seo的支持和首屏的加载速度,所以服务端渲染是必不可少的。目前大部分前端项目都是用react或者vue框架开发的。如果用nodejs来承担服务端渲染的任务,那么就可以保证一套代码既可以做客户端渲染也可以做服务端渲染,而这些工作可以由前端程序员独立完成。服务端渲染技术很重要,后面会单独一节讲解。总结一下,nodejs作为中间层最有价值的功能就是服务端渲染和接口数据聚合。如果企业应用数量少,业务简单,规模不大,不建议加中间层,会把简单的事情搞复杂。