欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React?SSR?中的限流案例詳解

 更新時(shí)間:2022年07月07日 17:12:56   作者:Ayou  
這篇文章主要介紹了React?SSR?之限流,React SSR 畢竟涉及到了服務(wù)端,有很多服務(wù)端特有的問題需要考慮,而限流就是其中之一,本文會(huì)通過一個(gè)簡(jiǎn)單的案例來說明,為什么服務(wù)端需要進(jìn)行限流,需要的朋友可以參考下

當(dāng)對(duì) React 應(yīng)用進(jìn)行頁面加載或 SEO 優(yōu)化時(shí),我們一般繞不開 React SSR。但 React SSR 畢竟涉及到了服務(wù)端,有很多服務(wù)端特有的問題需要考慮,而限流就是其中之一。

所謂限流,就是當(dāng)我們的服務(wù)資源有限、處理能力有限時(shí),通過對(duì)請(qǐng)求或并發(fā)數(shù)進(jìn)行限制從而保障系統(tǒng)正常運(yùn)行的一種策略。本文會(huì)通過一個(gè)簡(jiǎn)單的案例來說明,為什么服務(wù)端需要進(jìn)行限流。

為什么要限流

如下所示是一個(gè)簡(jiǎn)單的 nodejs 服務(wù)端項(xiàng)目:

const express = require('express')
const app = express()
app.get('/', async (req, res) => {
  // 模擬 SSR 會(huì)大量的占用內(nèi)存
  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 過程會(huì)大量的占用內(nèi)存的情況。

然后,通過 docker build -t ssr . 指定將我們的項(xiàng)目打包成一個(gè)鏡像,并通過以下命令運(yùn)行一個(gè)容器:

docker run \
-it \
-m 512m \ # 限制容器的內(nèi)存
--rm \
-p 2048:2048 \
--name ssr \
--oom-kill-disable \
ssr

我們將容器內(nèi)存限制在 512m,并通過 --oom-kill-disable 指定容器內(nèi)存不足時(shí)不關(guān)閉容器。

接下來,我們通過 autocannon 來進(jìn)行一下壓測(cè):

autocannon -c 10 -d 1000 http://localhost:2048

通過, docker stats 可以看到容器的運(yùn)行情況:

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

此時(shí),容器內(nèi)存已經(jīng)全部被占用,服務(wù)對(duì)外失去了響應(yīng),通過 curl -m 5 http://localhost:2048 訪問,收到了超時(shí)的錯(cuò)誤提示:

curl: (28) Operation timed out after 5001 milliseconds with 0 bytes received

我們改造一下代碼,使用 counter.js 來統(tǒng)計(jì) 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 = []
      // 刪除超出一秒的數(shù)據(jù)
      for (let i = 0, len = arr.length; i < len; i++) {
        if (now - arr[i].time > interval) continue
        newArr.push(arr[i])
      }
      arr = newArr
      return
    }
    // 計(jì)算前一秒的數(shù)據(jù)和
    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
  }
}

此時(shí),容器運(yùn)行正常:

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

雖然此時(shí)訪問 / 路由會(huì)收到錯(cuò)誤:

curl -m 5  http://localhost:2048
exceed limit

但是 /another 卻不受影響:

curl -m 5  http://localhost:2048/another
another api

由此可見,限流確實(shí)是系統(tǒng)進(jìn)行自我保護(hù)的一個(gè)比較好的方法。

令牌桶算法

常見的限流算法有“滑動(dòng)窗口算法”、“令牌桶算法”,我們這里討論 “令牌桶算法” 。在令牌桶算法中,存在一個(gè)桶,容量為 burst 。該算法以一定的速率(設(shè)為 rate )往桶中放入令牌,超過桶容量會(huì)丟棄。每次請(qǐng)求需要先獲取到桶中的令牌才能繼續(xù)執(zhí)行,否則拒絕。

根據(jù)令牌桶的定義,我們實(shí)現(xiàn)令牌桶算法如下:

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
}

簡(jiǎn)單解釋一下這個(gè)算法,調(diào)用 take 時(shí),會(huì)先執(zhí)行 refill 先往桶中進(jìn)行填充。填充的方式也很簡(jiǎn)單,首先計(jì)算出與上次填充的時(shí)間間隔 elapse 毫秒,然后計(jì)算出這段時(shí)間內(nèi)應(yīng)該補(bǔ)充的令牌數(shù),因?yàn)榱钆蒲a(bǔ)充速率是 rate 個(gè)/秒,所以需要補(bǔ)充的令牌數(shù)為:

elapse * (this.rate / 1000)

又因?yàn)榱钆茢?shù)不能超過桶的容量,所以補(bǔ)充后桶中的令牌數(shù)為:

Math.min(this.burst, this.tokens + elapse * (this.rate / 1000))

注意,這個(gè)令牌數(shù)是可以為小數(shù)的。

令牌桶算法具有以下兩個(gè)特點(diǎn):

  • 當(dāng)外部請(qǐng)求的 QPS M 大于令牌補(bǔ)充的速率 rate 時(shí),長(zhǎng)期來看,最終有效的 QPS 會(huì)趨向于 rate 。這個(gè)很好理解,拉的總不可能比吃的多吧。
  • 因?yàn)榱钆仆翱梢源嫦?nbsp;burst 個(gè)令牌,所以可以允許短時(shí)間的激增流量,持續(xù)的時(shí)間為:
T = burst / (M - rate) // rate < M

可以理解為一個(gè)水池里面有 burst 的水量,進(jìn)水的速率為 rate ,出水的速率為 M ,則凈出水速率為 M-rate ,則水池中的水放空的時(shí)間即為激增流量的持續(xù)時(shí)間。

到此這篇關(guān)于React SSR 之限流的文章就介紹到這了,更多相關(guān)React SSR內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React構(gòu)建簡(jiǎn)潔強(qiáng)大可擴(kuò)展的前端項(xiàng)目架構(gòu)

    React構(gòu)建簡(jiǎn)潔強(qiáng)大可擴(kuò)展的前端項(xiàng)目架構(gòu)

    這篇文章主要為大家介紹了React構(gòu)建簡(jiǎn)潔強(qiáng)大可擴(kuò)展的前端項(xiàng)目架構(gòu)實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • create-react-app開發(fā)常用配置教程

    create-react-app開發(fā)常用配置教程

    這篇文章主要為大家介紹了create-react-app開發(fā)常用配置教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • react hooks實(shí)現(xiàn)原理解析

    react hooks實(shí)現(xiàn)原理解析

    這篇文章主要介紹了react hooks實(shí)現(xiàn)原理,文中給大家介紹了useState dispatch 函數(shù)如何與其使用的 Function Component 進(jìn)行綁定,節(jié)后實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • 詳解使用React.memo()來優(yōu)化函數(shù)組件的性能

    詳解使用React.memo()來優(yōu)化函數(shù)組件的性能

    本文講述了開發(fā)React應(yīng)用時(shí)如何使用shouldComponentUpdate生命周期函數(shù)以及PureComponent去避免類組件進(jìn)行無用的重渲染,以及如何使用最新的React.memo API去優(yōu)化函數(shù)組件的性能
    2019-03-03
  • 使用React?SSR寫Demo一學(xué)就會(huì)

    使用React?SSR寫Demo一學(xué)就會(huì)

    這篇文章主要為大家介紹了使用React?SSR寫Demo實(shí)現(xiàn)教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • 最新版React?Native環(huán)境搭建(親測(cè))

    最新版React?Native環(huán)境搭建(親測(cè))

    這篇文章主要介紹了最新版React?Native環(huán)境搭建,React Native的運(yùn)行需要依賴原生Android和iOS環(huán)境,因此需要分別安裝原生Android和iOS的開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • 解決React報(bào)錯(cuò)Functions are not valid as a React child

    解決React報(bào)錯(cuò)Functions are not valid as 

    這篇文章主要為大家介紹了React報(bào)錯(cuò)Functions are not valid as a React child解決詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • react?hooks?d3實(shí)現(xiàn)企查查股權(quán)穿透圖結(jié)構(gòu)圖效果詳解

    react?hooks?d3實(shí)現(xiàn)企查查股權(quán)穿透圖結(jié)構(gòu)圖效果詳解

    這篇文章主要為大家介紹了react?hooks?d3實(shí)現(xiàn)企查查股權(quán)穿透圖結(jié)構(gòu)圖效果詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • React Router v4 入坑指南(小結(jié))

    React Router v4 入坑指南(小結(jié))

    這篇文章主要介紹了React Router v4 入坑指南(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04
  • React自定義Hook-useForkRef的具體使用

    React自定義Hook-useForkRef的具體使用

    本文主要介紹了React自定義Hook-useForkRef的具體使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論