溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React?SSR中的限流怎么實現

發布時間:2022-07-08 09:39:28 來源:億速云 閱讀:175 作者:iii 欄目:開發技術

這篇文章主要介紹“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))

注意,這個令牌數是可以為小數的。

令牌桶算法具有以下兩個特點:

  • 當外部請求的 QPS M 大于令牌補充的速率 rate 時,長期來看,最終有效的 QPS 會趨向于 rate 。這個很好理解,拉的總不可能比吃的多吧。

  • 因為令牌桶可以存下 burst 個令牌,所以可以允許短時間的激增流量,持續的時間為:

T = burst / (M - rate) // rate < M

可以理解為一個水池里面有 burst 的水量,進水的速率為 rate ,出水的速率為 M ,則凈出水速率為 M-rate ,則水池中的水放空的時間即為激增流量的持續時間。

關于“React SSR中的限流怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女