>웹 프론트엔드 >JS 튜토리얼 >nodejs 미들웨어를 실습으로 분석한 글

nodejs 미들웨어를 실습으로 분석한 글

青灯夜游
青灯夜游앞으로
2022-09-27 20:36:591988검색

nodejs 미들웨어를 실습으로 분석한 글

nodejs의 등장으로 프런트엔드 업계에 무한한 가능성이 생겼고, 원래는 클라이언트 개발만 담당하던 많은 학생들이 서서히 서버사이드를 접하고 사용하게 되었습니다. nodejs的出现为前端行业带来了无限的可能性,让很多原来只负责客户端开发的同学也慢慢开始接触和使用服务器端技术.

虽然nodejs带来了很多的好处,但是它也存在自身的局限性.和那些传统老牌的编程语言相比,如JAVA,PHP.nodejs并不能成为它们的替代品,而且在可预估的未来,也很难撼动那些老牌编程语言的地位。【相关教程推荐:nodejs视频教程

目前nodejs主要有以下几个应用场景.

  • 前端工程化,比如rollup,webpack在工程化方向的探索
  • nodejs中间层
  • 客户端集成nodejs,比如electron
  • 市面上一些不太复杂的应用选择nodejs作为后端编程语言

本文主要讲一讲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,然后就会向这个路径发起请求,得到响应后再返回给浏览器.

接口聚合

上面介绍的接口转发在实践中很少会单独应用,如果仅仅只是为了转发一下数据,那还不如直接用nginx

nodejs는 많은 이점을 제공하지만 JAVA, PHP.<code>nodejs는 이를 대체할 수 없으며 가까운 미래에 이러한 오래된 프로그래밍 언어의 지위를 흔들기는 어려울 것입니다. [추천 관련 튜토리얼: nodejs 동영상 튜토리얼]

현재 nodejs주로 다음과 같은 응용 시나리오가 있습니다. <p></p> <ul> <li>엔지니어링 방향의 <code>롤업, webpack 탐색과 같은 프런트엔드 엔지니어링
  • nodejs중간 계층
  • electron과 같은 클라이언트 통합 nodejs
  • 시장 일부 덜 복잡한 애플리케이션은 백엔드 프로그래밍 언어로 nodejs를 선택합니다.
  • 이 기사에서는 주로 nodejs를 중간으로 사용하는 일부 사례에 대해 설명합니다. 레이어, 아래 그림을 확인하세요.🎜🎜1. png 🎜🎜기존 개발 모델에서는 브라우저가 서버 레이어와 직접 통신합니다. 중간 레이어가 추가된다는 것은 브라우저와 서버 사이에 레이어가 추가된다는 의미입니다. .🎜🎜클라이언트가 <code>Server에 직접 요청을 보내는 것으로 나타났습니다. 요청을 받은 후 Server 레이어는 결과를 계산하고 처리합니다. 결과가 브라우저에 반환됩니다.🎜🎜이제 브라우저는 요청을 보냅니다. 노드 레이어에 대한 처리 라운드 후, 노드 레이어노드 레이어에 대한 요청을 시작합니다. >서버 레이어. 서버 레이어가 처리됩니다. 응답 결과를 노드 레이어, 노드 레이어에 반환하고 마지막으로 데이터를 반환합니다. 브라우저.🎜🎜노드 레이어, 의 등장으로 인해 서버 레이어는 프런트 엔드의 필드에 대한 특별한 요구 사항에 신경 쓰지 않고 비즈니스 자체에만 집중할 수 있습니다. 🎜🎜<code>노드 레이어서버 레이어에서 데이터를 얻은 다음 프런트엔드 UI의 요구 사항을 충족하는 데이터 형식으로 데이터를 계산하고 통합할 수 있습니다. 또한 전체 애플리케이션이 마이크로서비스 아키텍처를 채택하는 경우 서버 계층에는 별도의 비즈니스 모듈을 관리하는 많은 서버가 있고 노드 계층은 마이크로서비스 아키텍처에 잘 적응하여 여러 서버에 대한 요청을 시작하여 다양한 모듈에서 데이터를 얻은 다음 이를 통합하고 변환하여 프런트 엔드로 보낼 수 있습니다. 🎜🎜다음은 nodejs의 몇 가지 사례를 소개하는 데 중점을 둡니다. 🎜<h2><strong>프록시 전달</strong></h2>🎜프록시 전달에는 실제로 널리 사용되는 응용 프로그램이 많이 있습니다. 브라우저는 먼저 요청을 <code>노드 서버로 보냅니다. > 요청을 받은 후 노드 서버에서 원래 경로 변경, 요청 헤더 정보 변경, 수정된 요청을 원격 서버로 보내는 등 요청에 대한 일부 처리를 수행할 수 있습니다. 실제 서버입니다.🎜🎜원격 서버는 응답 결과를 계산하여 노드 서버로 반환하지만, 노드 서버는 여전히 응답을 선택적으로 처리하여 브라우저로 반환할 수 있습니다 🎜🎜. 프록시 전달은 일상적인 프런트 엔드 개발에서 자주 발생하는 도메인 간 문제를 해결할 수 있으며, 또한 원격 실제 서버의 세부 정보를 숨겨 브라우저가 노드 서버와만 통신할 수 있도록 합니다. 🎜
    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);
    🎜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가 되며, 이 경로는 요청을 시작하고 브라우저에 응답을 반환합니다. h2>인터페이스 집계🎜 위에서 소개한 인터페이스 전달은 실제로 단독으로 사용되는 경우가 거의 없으며 단지 데이터를 전달하기 위한 경우에는 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做中间层最有价值的功能是服务器端渲染和接口数据聚合.如果企业应用数量较少业务简单还没有规模化,不建议添加中间层,那样反而让简单的事情变得复杂.

    更多node相关知识,请访问:nodejs 教程

    위 내용은 nodejs 미들웨어를 실습으로 분석한 글의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제