<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當對 React 應用進行頁面載入或 SEO 優化時,我們一般繞不開 React SSR。但 React SSR 畢竟涉及到了伺服器端,有很多伺服器端特有的問題需要考慮,而限流就是其中之一。
所謂限流,就是當我們的服務資源有限、處理能力有限時,通過對請求或並行數進行限制從而保障系統正常執行的一種策略。本文會通過一個簡單的案例來說明,為什麼伺服器端需要進行限流。
如下所示是一個簡單的 nodejs 伺服器端專案:
const express = require('express') const app = express() app.get('/', async (req, res) => { // 模擬 SSR 會大量的佔用記憶體 const buf = Buffer.alloc(1024 * 1024 * 200, 'a') console.log(buf) res.end('end') }) app.get('/another', async (req, res) => { res.end('another api') }) const listener = app.listen(process.env.PORT || 2048, () => { console.log('Your app is listening on port ' + listener.address().port) })
其中,我們通過 Buffer
來模擬 SSR 過程會大量的佔用記憶體的情況。
然後,通過 docker build -t ssr .
指定將我們的專案打包成一個映象,並通過以下命令執行一個容器:
docker run -it -m 512m # 限制容器的記憶體 --rm -p 2048:2048 --name ssr --oom-kill-disable ssr
我們將容器記憶體限制在 512m,並通過 --oom-kill-disable
指定容器記憶體不足時不關閉容器。
接下來,我們通過 autocannon
來進行一下壓測:
autocannon -c 10 -d 1000 http://localhost:2048
通過, docker stats
可以看到容器的執行情況:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d9c0189e2b56 ssr 0.00% 512MiB / 512MiB 99.99% 14.6kB / 8.65kB 41.9MB / 2.81MB 40
此時,容器記憶體已經全部被佔用,服務對外失去了響應,通過 curl -m 5 http://localhost:2048
存取,收到了超時的錯誤提示:
curl: (28) Operation timed out after 5001 milliseconds with 0 bytes received
我們改造一下程式碼,使用 counter.js
來統計 QPS,並限制為 2:
const express = require('express') const counter = require('./counter.js') const app = express() const limit = 2 let cnt = counter() app.get( '/', (req, res, next) => { cnt(1) if (cnt() > limit) { res.writeHead(500, { 'content-type': 'text/pain', }) res.end('exceed limit') return } next() }, async (req, res) => { const buf = Buffer.alloc(1024 * 1024 * 200, 'a') console.log(buf) res.end('end') } ) app.get('/another', async (req, res) => { res.end('another api') }) const listener = app.listen(process.env.PORT || 2048, () => { console.log('Your app is listening on port ' + listener.address().port) }) // counter.js module.exports = function counter(interval = 1000) { let arr = [] return function cnt(number) { const now = Date.now() if (number > 0) { arr.push({ time: now, value: number, }) const newArr = [] // 刪除超出一秒的資料 for (let i = 0, len = arr.length; i < len; i++) { if (now - arr[i].time > interval) continue newArr.push(arr[i]) } arr = newArr return } // 計算前一秒的資料和 let sum = 0 for (let i = arr.length - 1; i >= 0; i--) { const {time, value} = arr[i] if (now - time <= interval) { sum += value continue } break } return sum } }
此時,容器執行正常:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3bd5aa07a3a7 ssr 88.29% 203.1MiB / 512MiB 39.67% 24.5MB / 48.6MB 122MB / 2.81MB 40
雖然此時存取 /
路由會收到錯誤:
curl -m 5 http://localhost:2048 exceed limit
但是 /another
卻不受影響:
curl -m 5 http://localhost:2048/another another api
由此可見,限流確實是系統進行自我保護的一個比較好的方法。
常見的限流演演算法有“滑動視窗演演算法”、“令牌桶演演算法”,我們這裡討論 “令牌桶演演算法” 。在令牌桶演演算法中,存在一個桶,容量為 burst
。該演演算法以一定的速率(設為 rate
)往桶中放入令牌,超過桶容量會丟棄。每次請求需要先獲取到桶中的令牌才能繼續執行,否則拒絕。
根據令牌桶的定義,我們實現令牌桶演演算法如下:
export default class TokenBucket { private burst: number private rate: number private lastFilled: number private tokens: number constructor(burst: number, rate: number) { this.burst = burst this.rate = rate this.lastFilled = Date.now() this.tokens = burst } setBurst(burst: number) { this.burst = burst return this } setRate(rate: number) { this.rate = rate return this } take() { this.refill() if (this.tokens > 0) { this.tokens -= 1 return true } return false } refill() { const now = Date.now() const elapse = now - this.lastFilled this.tokens = Math.min(this.burst, this.tokens + elapse * (this.rate / 1000)) this.lastFilled = now } }
然後,按照如下方式使用:
const tokenBucket = new TokenBucket(5, 10) if (tokenBucket.take()) { // Do something } else { // refuse }
簡單解釋一下這個演演算法,呼叫 take
時,會先執行 refill
先往桶中進行填充。填充的方式也很簡單,首先計算出與上次填充的時間間隔 elapse
毫秒,然後計算出這段時間內應該補充的令牌數,因為令牌補充速率是 rate
個/秒,所以需要補充的令牌數為:
elapse * (this.rate / 1000)
又因為令牌數不能超過桶的容量,所以補充後桶中的令牌數為:
Math.min(this.burst, this.tokens + elapse * (this.rate / 1000))
注意,這個令牌數是可以為小數的。
令牌桶演演算法具有以下兩個特點:
M
大於令牌補充的速率 rate
時,長期來看,最終有效的 QPS 會趨向於 rate
。這個很好理解,拉的總不可能比吃的多吧。burst
個令牌,所以可以允許短時間的激增流量,持續的時間為:T = burst / (M - rate) // rate < M
可以理解為一個水池裡面有 burst
的水量,進水的速率為 rate
,出水的速率為 M
,則淨出水速率為 M-rate
,則水池中的水放空的時間即為激增流量的持續時間。
到此這篇關於React SSR 之限流的文章就介紹到這了,更多相關React SSR內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45