您好,登錄后才能下訂單哦!
這篇文章主要介紹了nodejs中間件使用實(shí)例分析的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇nodejs中間件使用實(shí)例分析文章都會(huì)有所收獲,下面我們一起來看看吧。
nodejs
的出現(xiàn)為前端行業(yè)帶來了無限的可能性,讓很多原來只負(fù)責(zé)客戶端開發(fā)的同學(xué)也慢慢開始接觸和使用服務(wù)器端技術(shù).
雖然nodejs
帶來了很多的好處,但是它也存在自身的局限性.和那些傳統(tǒng)老牌的編程語言相比,如JAVA
,PHP
.nodejs
并不能成為它們的替代品,而且在可預(yù)估的未來,也很難撼動(dòng)那些老牌編程語言的地位。
目前nodejs
主要有以下幾個(gè)應(yīng)用場(chǎng)景.
前端工程化,比如rollup
,webpack
在工程化方向的探索
nodejs
中間層
客戶端集成nodejs
,比如electron
市面上一些不太復(fù)雜的應(yīng)用選擇nodejs
作為后端編程語言
本文主要講一講nodejs
作為中間層的一些實(shí)踐,查看下圖.
傳統(tǒng)的的開發(fā)模式由瀏覽器直接和Server
層直接通信,中間層的加入意味著在瀏覽器和Server
層之間額外添加了一層.
原來客戶端直接向Server
發(fā)送請(qǐng)求,Server
層收到請(qǐng)求后經(jīng)過計(jì)算處理將結(jié)果返回給瀏覽器.
如今瀏覽器將請(qǐng)求發(fā)送給node層
,node層
經(jīng)過一輪處理后再向Server層
發(fā)起請(qǐng)求.Server層
處理完畢將響應(yīng)結(jié)果返回給node層
,node層
最后將數(shù)據(jù)返回給瀏覽器.
因?yàn)?code>node層的出現(xiàn),Server層
可以只用關(guān)注業(yè)務(wù)本身,而不必理會(huì)前端對(duì)字段的特殊要求。
node層
可以向server
層獲取數(shù)據(jù),再通過對(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ā)在實(shí)際中有很多廣泛的應(yīng)用.瀏覽器首先將請(qǐng)求發(fā)送給node服務(wù)器
,請(qǐng)求收到后node服務(wù)器
可以對(duì)請(qǐng)求做一些處理,比如將原來的路徑變換一下,請(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ā)可以解決前端日常開發(fā)中經(jīng)常遇到的跨域問題,另外它還屏蔽了遠(yuǎn)程真實(shí)服務(wù)器的細(xì)節(jié),讓瀏覽器只與node服務(wù)器
通信.下面是簡單的實(shí)踐.
登錄后復(fù)制const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();//創(chuàng)建應(yīng)用
app.use("/api",createProxyMiddleware( //設(shè)置代理轉(zhuǎn)發(fā)
{
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
是一個(gè)第三方依賴包,可以非常方便設(shè)置代理轉(zhuǎn)發(fā),需要通過npm
安裝.
如果當(dāng)前訪問的路徑是以/api
開頭,那么該請(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)前瀏覽器訪問http://localhost:3000/api/list
.因?yàn)檫@個(gè)路徑以/api
開頭所以會(huì)被攔截,從而觸發(fā)pathRewrite
函數(shù)修改訪問路徑.最終訪問路徑就變成了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è)銷售體系,一個(gè)是線上的電商平臺(tái)銷售,另一個(gè)是線下實(shí)體店.它們分別屬于不同的團(tuán)隊(duì)運(yùn)營,維護(hù)著不同的數(shù)據(jù)系統(tǒng).
如果當(dāng)前請(qǐng)求只是想查詢一下電商平臺(tái)某款商品的信息,只需要將接口轉(zhuǎn)發(fā)給電商平臺(tái)系統(tǒng)即可.同理如果僅僅只是查詢線下實(shí)體店某一天的銷售業(yè)績,可以直接把請(qǐng)求轉(zhuǎn)發(fā)給線下數(shù)據(jù)系統(tǒng)查詢,再把響應(yīng)數(shù)據(jù)返回.上面介紹的插件http-proxy-middleware
支持配置多個(gè)代理路徑,詳細(xì)可查詢文檔.
現(xiàn)在有這么一個(gè)需求,目標(biāo)是查詢本周某款商品在線上和線下銷售數(shù)據(jù)的對(duì)比.那么這個(gè)時(shí)候就需要node層
向兩個(gè)遠(yuǎn)程服務(wù)器發(fā)送請(qǐng)求分別獲取線上銷售數(shù)據(jù)和線下銷售數(shù)據(jù),將這兩部分?jǐn)?shù)據(jù)聚合處理后再返回給前端.簡單實(shí)踐如下.
登錄后復(fù)制const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();//創(chuàng)建應(yīng)用
//偽代碼
app.get("/getSaleInfo",async (req,res)=>{
const online_data = await getOnline(); //獲取線上數(shù)據(jù)
const offline_data = await getOffline(); //獲取線下數(shù)據(jù)
res.send(dataHanlder(online_data,offline_data)); //對(duì)數(shù)據(jù)處理后返回給前端
})
proxyHanlder(app);//偽代碼,將代理轉(zhuǎn)發(fā)的邏輯封裝起來
app.use("*",(req,res)=>{
res.send("hello world");
})
app.listen(3000);
/getSaleInfo
代表著將兩條數(shù)據(jù)聚合的自定義路由,如果需要聚合數(shù)據(jù)的需求比較多,這塊邏輯要單獨(dú)封裝到路由模塊中管理,并且要寫在代理轉(zhuǎn)發(fā)的前面.
這樣就確保了需要轉(zhuǎn)發(fā)的接口就交給轉(zhuǎn)發(fā)的邏輯處理,需要個(gè)性化處理數(shù)據(jù)的接口就單獨(dú)編寫路由操作數(shù)據(jù).
緩存對(duì)于提升系統(tǒng)性能,減小數(shù)據(jù)庫壓力起到了無足輕重的作用.一般常用的緩存軟件是redis
,它可以被理解成數(shù)據(jù)存儲(chǔ)在內(nèi)存當(dāng)中的數(shù)據(jù)庫.由于數(shù)據(jù)放在內(nèi)存中,讀寫速度非常快,能極快的響應(yīng)用戶的請(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
開始請(qǐng)求server
層得到響應(yīng)結(jié)果,此時(shí)在將響應(yīng)結(jié)果返回給瀏覽器之前,將該次請(qǐng)求的訪問路徑作為key
值,響應(yīng)結(jié)果作為value
存儲(chǔ)到redis
中.這樣之后再有相同的請(qǐng)求發(fā)來時(shí),先查看redis
有沒有緩存該請(qǐng)求的數(shù)據(jù),如果緩存了直接將數(shù)據(jù)返回,如果沒有緩存再去請(qǐng)求server
層,把上述流程再走一遍.
redis
還可以對(duì)緩存數(shù)據(jù)設(shè)置過期時(shí)間和清除,可以根據(jù)具體的業(yè)務(wù)操作.簡單實(shí)踐如下.
登錄后復(fù)制const express = require('express');
const app = express();//創(chuàng)建應(yīng)用
//偽代碼
app.use("*",(req,res,next)=>{
const path = req.originalUrl; //獲取訪問路徑
if(redisClient.getItem(path)){ //查看redis中有沒有緩存該條接口的數(shù)據(jù)
res.send(redisClient.getItem(path)); // 返回緩存數(shù)據(jù)
}else{
next(); //不執(zhí)行任何操作,直接放行
}
})
aggregate(app); //偽代碼,將接口聚合的邏輯封裝起來
proxyHanlder(app);//偽代碼,將代理轉(zhuǎn)發(fā)的邏輯封裝起來
app.use("*",(req,res)=>{
res.send("hello world");
})
app.listen(3000);
node
做中間層可以對(duì)前端無節(jié)制的訪問做限制.比如有些惡意的腳本循環(huán)訪問接口,一秒鐘訪問幾十次增大了服務(wù)器的負(fù)載.
redis
可以幫助我們實(shí)現(xiàn)這一功能.用戶第一次訪問,解析出本次請(qǐng)求的ip
地址,將ip
作為key
值,value
置為0
存到redis
中.
用戶第二次訪問,取出ip
找到redis
中對(duì)應(yīng)的value
,然后自增1
.如果是相同的人重復(fù)大量訪問,value
在短期內(nèi)就自增到了很大的數(shù)字,我們可以每次獲取這個(gè)數(shù)字判端是否超過了設(shè)定的預(yù)期標(biāo)準(zhǔn),超過則拒絕本次請(qǐng)求.簡單實(shí)踐如下.
登錄后復(fù)制const express = require('express');
const app = express();//創(chuàng)建應(yīng)用
//偽代碼
app.use("*",(req,res,next)=>{
const ip = req.ip;
let num = 0;
if(redisClient.getItem(ip)){ //是否緩存了當(dāng)前的ip字段
num = redisClient.incr(ip); //每訪問一下,計(jì)數(shù)加1
}else{
redisClient.setItem(ip,0);
redisClient.setExpireTime(5); //設(shè)置過期時(shí)間為5秒,5秒后再獲取該ip為空
}
if(num > 20){
res.send("非法訪問");
}else{
next();//放行
}
})
cacheData(app)//偽代碼.緩存接口數(shù)據(jù)
aggregate(app); //偽代碼,將接口聚合的邏輯封裝起來
proxyHanlder(app);//偽代碼,將代理轉(zhuǎn)發(fā)的邏輯封裝起來
app.use("*",(req,res)=>{
res.send("hello world");
})
app.listen(3000);
在應(yīng)用的前面設(shè)置一層限流中間件,每次訪問來臨先判端是否緩存過.第一次訪問肯定沒有緩存,就將當(dāng)前ip
對(duì)應(yīng)的值設(shè)置為0
并添加過期時(shí)間為5
秒鐘.下一次相同的用戶再訪問時(shí)就會(huì)將value
自增1
.
最后的效果就達(dá)到了5
秒內(nèi)調(diào)用接口的次數(shù)超過20
次便拒絕訪問.
系統(tǒng)沒有日志,相當(dāng)于人沒有雙眼.日志可以幫助我們發(fā)現(xiàn)分析定位線上系統(tǒng)出現(xiàn)的錯(cuò)誤.另外通過日志數(shù)據(jù)也可以進(jìn)行統(tǒng)計(jì)計(jì)算得出某些結(jié)論和趨勢(shì).
node
層能夠承擔(dān)起管理日志的功能,以接口訪問日志為例.在系統(tǒng)中新建一個(gè)日志文件夾,每次有請(qǐng)求訪問時(shí),首先解析請(qǐng)求的路徑、當(dāng)前的訪問時(shí)間以及攜帶的參數(shù)和終端數(shù)據(jù)信息.然后在日志文件夾創(chuàng)建一個(gè)txt
文件存放當(dāng)天日志情況,將上述數(shù)據(jù)和該請(qǐng)求的響應(yīng)結(jié)果組合成一條記錄插入txt
文件中.下一次訪問繼續(xù)走上面流程往txt
文件添加訪問日志.像上面介紹的代理轉(zhuǎn)發(fā),插件http-proxy-middleware
支持配置如何返回響應(yīng)結(jié)果,那么在相應(yīng)的事件函數(shù)鉤子里就可以同時(shí)得到請(qǐng)求和響應(yīng),有了這兩塊數(shù)據(jù)就可以存放到日志中.
這里還能制定很多的配置策略.可以選擇一天一個(gè)日志文本,如果訪問量巨大也可以選擇一個(gè)小時(shí)一個(gè)日志文本,依據(jù)實(shí)際情況而定.
另外隨著時(shí)間的延長,日志文件夾的文件內(nèi)容會(huì)越來越多.這就需要編寫linux
操作系統(tǒng)定時(shí)任務(wù)來遷移和備份這些日志數(shù)據(jù).
日志操作簡單實(shí)踐如下.
登錄后復(fù)制//偽代碼
app.use("/getList",async (req,res)=>{
const list = await getProductList(); //獲取商品數(shù)據(jù)
const { 訪問時(shí)間,訪問路徑,參數(shù) } = req;
logger.log('info',`${訪問時(shí)間}-${訪問路徑和參數(shù)}:${list}`);//將數(shù)據(jù)存儲(chǔ)到日志文件中
res.send(list);//將結(jié)果返回給客戶端
})
關(guān)于“nodejs中間件使用實(shí)例分析”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“nodejs中間件使用實(shí)例分析”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。