Express - Rate Limiting & Caching


1.Rate Limiting&Caching概要

  • リクエストに対して一定時間の同じ応答が必要である場合、クライアントがmax-age値に基づいて一定時間経過した後にキャッシュを使用するのではなく、expressのたびに応答が与えられる.
  • redisに期限切れが与えられた場合、その時間が期限切れになる前にredis値を
  • に転送する.
  • はDOS攻撃のように継続的な再要求を防ぐためにRate Limitingを適用することができる.
  • 2. Rate Limiting

    // index.js
    const express = require('express')
    const rateLimit = require('express-rate-limit')
    require('dotenv').config()
    
    const PORT = process.env.PORT || 5000
    
    const app = express()
    
    // // Rate limiting
    const limiter = rateLimit({
      windowMs: 10 * 60 * 1000, // 10 Mins
      max: 100,
    })
    app.use(limiter)
    app.set('trust proxy', 1)
    
    // // Routes
    app.use('/api', require('./routes'))
    
    app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
    
  • Express-使用率制限ライブラリ
  • リクエストが100回継続した場合、block
  • は10分間継続します.

    要求が
  • Postmanに送信されるたびにRemainが減少し、100回後10分以内にBlock
  • 3. local Caching

    // ./router/index.js
    
    const url = require('url')
    const express = require('express')
    const router = express.Router()
    const needle = require('needle')
    const apicache = require('apicache')
    
    const API_BASE_URL = process.env.API_BASE_URL
    const API_KEY_NAME = process.env.API_KEY_NAME
    const API_KEY_VALUE = process.env.API_KEY_VALUE
    
    let cache = apicache.middleware
    
    router.get('/', cache('1 minutes'), async(req, res) => {
      try{
    
        const params = new URLSearchParams({
          [API_KEY_NAME]:API_KEY_VALUE,
          ...url.parse(req.url, true).query
        })
    
        const apiRes = await needle('get', `${API_BASE_URL}?${params}`)
        const data = apiRes.body
    
        if(process.env.NODE_ENV !== 'production') {
          console.log(`요청: ${API_BASE_URL}?${params}`)
        }
    
        res.status(200).json(data);
      } catch(err) {
        res.status(500).json({err})
      }
    })
    
    module.exports = router

  • アプリケーションライブラリの使用

  • アプリケーションミドルウェアに設定して1分後、同じリクエストに対してクライアントはmax-ageを超えるまでキャッシュを使用します.

  • paramsを動的に調整する必要がある場合は、URLSearchParamsを使用します.
  • cache-control最大年齢
  • をサポート

    4. redis caching


    (1)Redisベース(参照)
    [基本]
    SET 'key' 'value'
    GET 'key'
    DEL 'key'
    keys*~すべての鍵を表示
    flushall~すべての鍵を削除
    setex「key」10「value」-10秒後に鍵を削除
    ttl「key」-expire残り時間クエリー
    [リスト]
    lplush「key」「value」(左側挿入)
    rplush「key」「value」(右側挿入)
    lrange「key」0-1(配列クエリー)
    lpop「key」(左側削除)
    rpop「key」(右削除)
    [Set]
    SADD「key」「value」(set挿入)
    SREM「key」「value」(set削除)
    SMEMBERS「key」(照会set)
    [Hash]
    HSET person name"value"(挿入)
    HSET personage"value"(挿入)
    HGETALL person(クエリー)
    HDEL personage(削除年齢)
    (2)Redisキャッシュの適用
    const express = require('express')
    const axios = require('axios')
    const Redis = require('redis')
    
    const redisClient = Redis.createClient()
    const DEFAULT_EXPIRATION = 5
    
    
    const PORT = process.env.PORT || 5000
    
    const app = express()
    
    app.use(express.urlencoded({extended: true}))
    
    app.get('/photos', async (req, res) => {
      
      const albumId = req.query.albumId
      const photos = await getOrSetCache(`photos?albumId=${albumId}`, async () => {
          const {data} = await axios.get(
            'https://jsonplaceholder.typicode.com/photos',
            {params: {albumId}}
          )
          return data;
      })
      res.json(photos);
      // redisClient.get(`photos?albumId=${albumId}`, async (err, photos) => {
      //   if(err) console.log(err)
      //   if(photos !== null) {
      //     return res.json(JSON.parse(photos))
      //   } else {
      //     const {data} = await axios.get(
      //       'https://jsonplaceholder.typicode.com/photos',
      //       {params: {albumId}}
      //     )
      //     redisClient.setex(`photos?albumId=${albumId}`, DEFAULT_EXPIRATION, JSON.stringify(data))
      //     res.json(data)
      //   }
      // })
    })
    
    function getOrSetCache(key, cb) {
      return new Promise((resolve, reject) => {
        redisClient.get(key, async (err, data) => {
          if(err) return reject(err)
          if(data !== null) return resolve(JSON.parse(data))
          const freshData = await cb()
          redisClient.setex(key, DEFAULT_EXPIRATION, JSON.stringify(freshData))
          resolve(freshData)
        })
      })
    }
    
    app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
    
  • 要求の結果値Redis key,photos?AlbuId=${albuId}にはデータが含まれ、expireを5秒
  • に設定します.
  • が期限切れになる前に、鍵の値
  • が返される.
    注意:APIプロキシサーバー-Hide Your APIキーを作成し、高速制限とキャッシュを実行します.