自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

淺談Nodejs中間層

開(kāi)發(fā) 前端
nodejs的出現(xiàn)為前端行業(yè)帶來(lái)了無(wú)限的可能性,讓很多原來(lái)只負(fù)責(zé)客戶(hù)端開(kāi)發(fā)的同學(xué)也慢慢開(kāi)始接觸和使用服務(wù)器端技術(shù).

 [[390852]]

前言

nodejs的出現(xiàn)為前端行業(yè)帶來(lái)了無(wú)限的可能性,讓很多原來(lái)只負(fù)責(zé)客戶(hù)端開(kāi)發(fā)的同學(xué)也慢慢開(kāi)始接觸和使用服務(wù)器端技術(shù).

雖然nodejs帶來(lái)了很多的好處,但是它也存在自身的局限性.和那些傳統(tǒng)老牌的編程語(yǔ)言相比,如JAVA,PHP.nodejs并不能成為它們的替代品,而且在可預(yù)估的未來(lái),也很難撼動(dòng)那些老牌編程語(yǔ)言的地位.

目前nodejs主要有以下幾個(gè)應(yīng)用場(chǎng)景.

  •  前端工程化,比如rollup,webpack在工程化方向的探索
  •  nodejs中間層
  •  客戶(hù)端集成nodejs,比如electron
  •  市面上一些不太復(fù)雜的應(yīng)用選擇nodejs作為后端編程語(yǔ)言

本文主要講一講nodejs作為中間層的一些實(shí)踐,查看下圖.

傳統(tǒng)的的開(kāi)發(fā)模式由瀏覽器直接和Server層直接通信,中間層的加入意味著在瀏覽器和Server層之間額外添加了一層.

原來(lái)客戶(hù)端直接向Server發(fā)送請(qǐng)求,Server層收到請(qǐng)求后經(jīng)過(guò)計(jì)算處理將結(jié)果返回給瀏覽器.

如今瀏覽器將請(qǐng)求發(fā)送給node層,node層經(jīng)過(guò)一輪處理后再向Server層發(fā)起請(qǐng)求.Server層處理完畢將響應(yīng)結(jié)果返回給node層,node層最后將數(shù)據(jù)返回給瀏覽器.

因?yàn)閚ode層的出現(xiàn),Server層可以只用關(guān)注業(yè)務(wù)本身,而不必理會(huì)前端對(duì)字段的特殊要求。

node層可以向server層獲取數(shù)據(jù),再通過(guò)對(duì)數(shù)據(jù)的計(jì)算整合轉(zhuǎn)換成符合前端UI要求的數(shù)據(jù)格式.另外整個(gè)應(yīng)用如果采用微服務(wù)架構(gòu),那么Server層會(huì)有很多臺(tái)管理單獨(dú)業(yè)務(wù)模塊的服務(wù)器,node層就很好的適配了微服務(wù)的架構(gòu),它可以向多臺(tái)服務(wù)器發(fā)起請(qǐng)求獲取到不同模塊的數(shù)據(jù)再整合轉(zhuǎn)化發(fā)送給前端.

下面著重介紹一下nodejs作為中間層的部分實(shí)踐.

代理轉(zhuǎn)發(fā)

代理轉(zhuǎn)發(fā)在實(shí)際中有很多廣泛的應(yīng)用.瀏覽器首先將請(qǐng)求發(fā)送給node服務(wù)器,請(qǐng)求收到后node服務(wù)器可以對(duì)請(qǐng)求做一些處理,比如將原來(lái)的路徑變換一下,請(qǐng)求頭的信息改變一下,再把修改后的請(qǐng)求發(fā)送給遠(yuǎn)程真實(shí)的服務(wù)器.

遠(yuǎn)程服務(wù)器計(jì)算出響應(yīng)結(jié)果再返回給node服務(wù)器,node服務(wù)器仍然可以對(duì)響應(yīng)做選擇性處理再分返回給瀏覽器.

代理轉(zhuǎn)發(fā)可以解決前端日常開(kāi)發(fā)中經(jīng)常遇到的跨域問(wèn)題,另外它還屏蔽了遠(yuǎn)程真實(shí)服務(wù)器的細(xì)節(jié),讓瀏覽器只與node服務(wù)器通信.下面是簡(jiǎn)單的實(shí)踐. 

  1. const express = require('express');  
  2. const { createProxyMiddleware } = require('http-proxy-middleware');  
  3. const app = express();//創(chuàng)建應(yīng)用   
  4. app.use("/api",createProxyMiddleware( //設(shè)置代理轉(zhuǎn)發(fā)  
  5.   {   
  6.      target: 'http://www.xxx.com', //舉例隨便寫(xiě)的地址  
  7.      changeOrigin: true, 
  8.      pathRewrite: function (path) {   
  9.        return path.replace('/api', '/server/api');  
  10.      }  
  11.   })  
  12. );  
  13. app.use("*",(req,res)=>{  //不是以'/api'開(kāi)頭的路由全部返回"hello world"  
  14.   res.send("hello world");  
  15. })  
  16. app.listen(3000); 

http-proxy-middleware是一個(gè)第三方依賴(lài)包,可以非常方便設(shè)置代理轉(zhuǎn)發(fā),需要通過(guò)npm安裝.

如果當(dāng)前訪問(wèn)的路徑是以/api開(kāi)頭,那么該請(qǐng)求就會(huì)被http-proxy-middleware攔截.觀察http-proxy-middleware里面配置的參數(shù).

  •  target代表遠(yuǎn)程真實(shí)服務(wù)器的地址.
  •  changeOrigin設(shè)置為true,表示將請(qǐng)求轉(zhuǎn)發(fā)到target地址上.
  •  pathRewrite是對(duì)請(qǐng)求路徑做一下處理,將/api轉(zhuǎn)換成/server/api.

上面的案例意思很明顯,假如當(dāng)前瀏覽器訪問(wèn)http://localhost:3000/api/list.因?yàn)檫@個(gè)路徑以/api開(kāi)頭所以會(huì)被攔截,從而觸發(fā)pathRewrite函數(shù)修改訪問(wèn)路徑.最終訪問(wèn)路徑就變成了http://www.xxx.com/server/api/list,然后就會(huì)向這個(gè)路徑發(fā)起請(qǐng)求,得到響應(yīng)后再返回給瀏覽器.

接口聚合

上面介紹的接口轉(zhuǎn)發(fā)在實(shí)踐中很少會(huì)單獨(dú)應(yīng)用,如果僅僅只是為了轉(zhuǎn)發(fā)一下數(shù)據(jù),那還不如直接用nginx配置一下,轉(zhuǎn)發(fā)就搞定了.

如果接口聚合和接口轉(zhuǎn)發(fā)都需要,那么從代碼層面去解決還是優(yōu)先考慮的方式.

接口聚合是什么意思呢?假設(shè)現(xiàn)在企業(yè)有兩個(gè)銷(xiāo)售體系,一個(gè)是線上的電商平臺(tái)銷(xiāo)售,另一個(gè)是線下實(shí)體店.它們分別屬于不同的團(tuán)隊(duì)運(yùn)營(yíng),維護(hù)著不同的數(shù)據(jù)系統(tǒng).

如果當(dāng)前請(qǐng)求只是想查詢(xún)一下電商平臺(tái)某款商品的信息,只需要將接口轉(zhuǎn)發(fā)給電商平臺(tái)系統(tǒng)即可.同理如果僅僅只是查詢(xún)線下實(shí)體店某一天的銷(xiāo)售業(yè)績(jī),可以直接把請(qǐng)求轉(zhuǎn)發(fā)給線下數(shù)據(jù)系統(tǒng)查詢(xún),再把響應(yīng)數(shù)據(jù)返回.上面介紹的插件http-proxy-middleware支持配置多個(gè)代理路徑,詳細(xì)可查詢(xún)文檔.

現(xiàn)在有這么一個(gè)需求,目標(biāo)是查詢(xún)本周某款商品在線上和線下銷(xiāo)售數(shù)據(jù)的對(duì)比.那么這個(gè)時(shí)候就需要node層向兩個(gè)遠(yuǎn)程服務(wù)器發(fā)送請(qǐng)求分別獲取線上銷(xiāo)售數(shù)據(jù)和線下銷(xiāo)售數(shù)據(jù),將這兩部分?jǐn)?shù)據(jù)聚合處理后再返回給前端.簡(jiǎn)單實(shí)踐如下. 

  1. const express = require('express');  
  2. const { createProxyMiddleware } = require('http-proxy-middleware');  
  3. const app = express();//創(chuàng)建應(yīng)用  
  4. //偽代碼  
  5. app.get("/getSaleInfo",async (req,res)=>{   
  6.    const online_data =  await getOnline(); //獲取線上數(shù)據(jù)  
  7.    const offline_data = await getOffline(); //獲取線下數(shù)據(jù)  
  8.    res.send(dataHanlder(online_data,offline_data)); //對(duì)數(shù)據(jù)處理后返回給前端  
  9. })  
  10. proxyHanlder(app);//偽代碼,將代理轉(zhuǎn)發(fā)的邏輯封裝起來(lái)  
  11. app.use("*",(req,res)=> 
  12.   res.send("hello world");  
  13. })  
  14. app.listen(3000); 

/getSaleInfo代表著將兩條數(shù)據(jù)聚合的自定義路由,如果需要聚合數(shù)據(jù)的需求比較多,這塊邏輯要單獨(dú)封裝到路由模塊中管理,并且要寫(xiě)在代理轉(zhuǎn)發(fā)的前面.

這樣就確保了需要轉(zhuǎn)發(fā)的接口就交給轉(zhuǎn)發(fā)的邏輯處理,需要個(gè)性化處理數(shù)據(jù)的接口就單獨(dú)編寫(xiě)路由操作數(shù)據(jù).

數(shù)據(jù)緩存

緩存對(duì)于提升系統(tǒng)性能,減小數(shù)據(jù)庫(kù)壓力起到了無(wú)足輕重的作用.一般常用的緩存軟件是redis,它可以被理解成數(shù)據(jù)存儲(chǔ)在內(nèi)存當(dāng)中的數(shù)據(jù)庫(kù).由于數(shù)據(jù)放在內(nèi)存中,讀寫(xiě)速度非???能極快的響應(yīng)用戶(hù)的請(qǐng)求.

在node層部署redis管理緩存數(shù)據(jù),可以提升整體應(yīng)用性能.但不是什么數(shù)據(jù)都建議存放在redis中,只有那些不經(jīng)常變動(dòng)的數(shù)據(jù)應(yīng)該設(shè)置成緩存.

比如商品的信息數(shù)據(jù),瀏覽器對(duì)某個(gè)商品發(fā)起請(qǐng)求,想查看該商品的詳情.請(qǐng)求第一次到達(dá)node層,redis此時(shí)是空的.那么node開(kāi)始請(qǐng)求server層得到響應(yīng)結(jié)果,此時(shí)在將響應(yīng)結(jié)果返回給瀏覽器之前,將該次請(qǐng)求的訪問(wèn)路徑作為key值,響應(yīng)結(jié)果作為value存儲(chǔ)到redis中.這樣之后再有相同的請(qǐng)求發(fā)來(lái)時(shí),先查看redis有沒(méi)有緩存該請(qǐng)求的數(shù)據(jù),如果緩存了直接將數(shù)據(jù)返回,如果沒(méi)有緩存再去請(qǐng)求server層,把上述流程再走一遍.

redis還可以對(duì)緩存數(shù)據(jù)設(shè)置過(guò)期時(shí)間和清除,可以根據(jù)具體的業(yè)務(wù)操作.簡(jiǎn)單實(shí)踐如下. 

  1. const express = require('express');  
  2. const app = express();//創(chuàng)建應(yīng)用  
  3. //偽代碼  
  4. app.use("*",(req,res,next)=> 
  5.    const path = req.originalUrl; //獲取訪問(wèn)路徑  
  6.    if(redisClient.getItem(path)){ //查看redis中有沒(méi)有緩存該條接口的數(shù)據(jù)  
  7.      res.send(redisClient.getItem(path)); // 返回緩存數(shù)據(jù)  
  8.    }else{  
  9.      next(); //不執(zhí)行任何操作,直接放行   
  10.    }  
  11. })  
  12. aggregate(app); //偽代碼,將接口聚合的邏輯封裝起來(lái)  
  13. proxyHanlder(app);//偽代碼,將代理轉(zhuǎn)發(fā)的邏輯封裝起來(lái)  
  14. app.use("*",(req,res)=> 
  15.   res.send("hello world");  
  16. })  
  17. app.listen(3000); 

接口限流

node做中間層可以對(duì)前端無(wú)節(jié)制的訪問(wèn)做限制.比如有些惡意的腳本循環(huán)訪問(wèn)接口,一秒鐘訪問(wèn)幾十次增大了服務(wù)器的負(fù)載.

redis可以幫助我們實(shí)現(xiàn)這一功能.用戶(hù)第一次訪問(wèn),解析出本次請(qǐng)求的ip地址,將ip作為key值,value置為0存到redis中.

用戶(hù)第二次訪問(wèn),取出ip找到redis中對(duì)應(yīng)的value,然后自增1.如果是相同的人重復(fù)大量訪問(wèn),value在短期內(nèi)就自增到了很大的數(shù)字,我們可以每次獲取這個(gè)數(shù)字判端是否超過(guò)了設(shè)定的預(yù)期標(biāo)準(zhǔn),超過(guò)則拒絕本次請(qǐng)求.簡(jiǎn)單實(shí)踐如下. 

  1. const express = require('express');  
  2. const app = express();//創(chuàng)建應(yīng)用  
  3. //偽代碼  
  4. app.use("*",(req,res,next)=> 
  5.   const ip = req.ip;  
  6.   let num = 0 
  7.   if(redisClient.getItem(ip)){ //是否緩存了當(dāng)前的ip字段 
  8.     num = redisClient.incr(ip); //每訪問(wèn)一下,計(jì)數(shù)加1  
  9.   }else{  
  10.     redisClient.setItem(ip,0);  
  11.     redisClient.setExpireTime(5); //設(shè)置過(guò)期時(shí)間為5秒,5秒后再獲取該ip為空  
  12.   }  
  13.   if(num > 20){   
  14.     res.send("非法訪問(wèn)");  
  15.   }else{  
  16.     next();//放行  
  17.   }  
  18. })  
  19. cacheData(app)//偽代碼.緩存接口數(shù)據(jù)   
  20.  
  21. aggregate(app); //偽代碼,將接口聚合的邏輯封裝起來(lái)  
  22. proxyHanlder(app);//偽代碼,將代理轉(zhuǎn)發(fā)的邏輯封裝起來(lái)  
  23. app.use("*",(req,res)=> 
  24.   res.send("hello world");  
  25. })  
  26. app.listen(3000); 

在應(yīng)用的前面設(shè)置一層限流中間件,每次訪問(wèn)來(lái)臨先判端是否緩存過(guò).第一次訪問(wèn)肯定沒(méi)有緩存,就將當(dāng)前ip對(duì)應(yīng)的值設(shè)置為0并添加過(guò)期時(shí)間為5秒鐘.下一次相同的用戶(hù)再訪問(wèn)時(shí)就會(huì)將value自增1.

最后的效果就達(dá)到了5秒內(nèi)調(diào)用接口的次數(shù)超過(guò)20次便拒絕訪問(wèn).

日志操作

系統(tǒng)沒(méi)有日志,相當(dāng)于人沒(méi)有雙眼.日志可以幫助我們發(fā)現(xiàn)分析定位線上系統(tǒng)出現(xiàn)的錯(cuò)誤.另外通過(guò)日志數(shù)據(jù)也可以進(jìn)行統(tǒng)計(jì)計(jì)算得出某些結(jié)論和趨勢(shì).

node層能夠承擔(dān)起管理日志的功能,以接口訪問(wèn)日志為例.在系統(tǒng)中新建一個(gè)日志文件夾,每次有請(qǐng)求訪問(wèn)時(shí),首先解析請(qǐng)求的路徑、當(dāng)前的訪問(wèn)時(shí)間以及攜帶的參數(shù)和終端數(shù)據(jù)信息.然后在日志文件夾創(chuàng)建一個(gè)txt文件存放當(dāng)天日志情況,將上述數(shù)據(jù)和該請(qǐng)求的響應(yīng)結(jié)果組合成一條記錄插入txt文件中.下一次訪問(wèn)繼續(xù)走上面流程往txt文件添加訪問(wèn)日志.像上面介紹的代理轉(zhuǎn)發(fā),插件http-proxy-middleware支持配置如何返回響應(yīng)結(jié)果,那么在相應(yīng)的事件函數(shù)鉤子里就可以同時(shí)得到請(qǐng)求和響應(yīng),有了這兩塊數(shù)據(jù)就可以存放到日志中.

這里還能制定很多的配置策略.可以選擇一天一個(gè)日志文本,如果訪問(wèn)量巨大也可以選擇一個(gè)小時(shí)一個(gè)日志文本,依據(jù)實(shí)際情況而定.

另外隨著時(shí)間的延長(zhǎng),日志文件夾的文件內(nèi)容會(huì)越來(lái)越多.這就需要編寫(xiě)linux操作系統(tǒng)定時(shí)任務(wù)來(lái)遷移和備份這些日志數(shù)據(jù).

日志操作簡(jiǎn)單實(shí)踐如下. 

  1. //偽代碼  
  2. app.use("/getList",async (req,res)=> 
  3.   const list = await getProductList(); //獲取商品數(shù)據(jù)  
  4.   const { 訪問(wèn)時(shí)間,訪問(wèn)路徑,參數(shù) } = req;  
  5.   logger.log('info',`${訪問(wèn)時(shí)間}-${訪問(wèn)路徑和參數(shù)}:${list}`);//將數(shù)據(jù)存儲(chǔ)到日志文件中   
  6.   res.send(list);//將結(jié)果返回給客戶(hù)端  
  7. }) 

結(jié)尾

中間層另外還可以做很多其他事情,比如監(jiān)控、鑒權(quán)和服務(wù)器端渲染(SSR).這部分由于內(nèi)容比較多可以單獨(dú)成章,網(wǎng)絡(luò)上也有大量如何實(shí)踐的文章,可搜索查閱學(xué)習(xí).

其實(shí)上面所談到的所有功能其他編程語(yǔ)言都可以做到,這也成為了很多人質(zhì)疑是否需要在架構(gòu)上額外再加一層的顧慮.

添加nodejs中間層,對(duì)于前端同學(xué)來(lái)說(shuō)肯定是好消息.因?yàn)樗茏屒岸顺袚?dān)更多的工作任務(wù),讓前端的業(yè)務(wù)比重變大.另外后端從此只需要關(guān)注自身業(yè)務(wù),前端繼續(xù)干著自己擅長(zhǎng)的事,從整體上是能提升開(kāi)發(fā)效率.

但從宏觀角度上看,架構(gòu)額外增加一層勢(shì)必會(huì)造成整個(gè)應(yīng)用性能上的損耗,另外在部署,測(cè)試層面都會(huì)增大運(yùn)維成本.

當(dāng)下前后端分離已經(jīng)成為了主流的開(kāi)發(fā)模式,很多類(lèi)型的應(yīng)用需要seo的支持以及首屏加載速度,因此服務(wù)器端渲染不可或缺.前端項(xiàng)目目前大多采用react或vue框架開(kāi)發(fā),如果用nodejs承擔(dān)服務(wù)器端渲染的任務(wù),那么可以確保一套代碼既可以做客戶(hù)端渲染也能支持服務(wù)器端渲染,而這些工作都可以讓前端程序員獨(dú)立來(lái)完成.服務(wù)器端渲染技術(shù)非常重要,后面會(huì)開(kāi)一個(gè)小節(jié)單獨(dú)講解.

綜上來(lái)看,nodejs做中間層最有價(jià)值的功能是服務(wù)器端渲染和接口數(shù)據(jù)聚合.如果企業(yè)應(yīng)用數(shù)量較少業(yè)務(wù)簡(jiǎn)單還沒(méi)有規(guī)模化,不建議添加中間層,那樣反而讓簡(jiǎn)單的事情變得復(fù)雜. 

 

責(zé)任編輯:龐桂玉 來(lái)源: 前端大全
相關(guān)推薦

2021-12-02 06:58:01

中間頁(yè)中間層編程

2019-01-30 08:14:28

協(xié)議區(qū)塊鏈堆棧

2024-08-08 14:50:00

模型數(shù)據(jù)

2009-07-30 13:07:49

ASP.NET中的三層

2022-09-19 08:01:13

美團(tuán)Leaf發(fā)號(hào)

2017-11-27 06:01:37

數(shù)據(jù)庫(kù)中間件中間層

2024-11-25 07:00:00

RedisMySQL數(shù)據(jù)庫(kù)

2022-01-04 20:34:00

數(shù)據(jù)安全Relay

2016-11-01 20:26:47

前端模板underscoreWeb

2019-07-04 15:00:32

PythonHTTP服務(wù)器

2017-10-17 09:15:06

Web服務(wù)器區(qū)別

2025-02-27 09:49:32

2018-02-07 10:08:02

應(yīng)用服務(wù)器網(wǎng)絡(luò)數(shù)據(jù)庫(kù)

2009-10-12 12:46:55

Linux內(nèi)核SCSI IO

2021-08-10 14:10:02

Nodejs后端開(kāi)發(fā)

2009-08-10 15:20:00

構(gòu)建JSP Servl

2023-01-04 15:24:46

ACE組件UI布局

2015-08-14 09:31:55

開(kāi)源Python服務(wù)器

2009-08-14 16:25:36

C#中間語(yǔ)言和元數(shù)據(jù)

2024-01-26 10:39:52

架構(gòu)視頻
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)