IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    在 Egg.js 中使用 Redis 缓存提升性能

    冷石发表于 2022-08-29 09:01:22
    love 0

    Redis 是一款开源的,基于 BSD 许可的,高级键值缓存和存储系统

    前言

    Redis 是一款开源的,基于 BSD 许可的,高级键值缓存和存储系统。Redis 的键包括 string,hash,list,set,sorted set,bitmap 和 hyperloglog。你可以在这些类型上面运行原子操作,例如,追加字符串,增加哈希中的值,加入一个元素到列表,计算集合的交集、并集和差集,或者是从有序集合中获取最高排名的元素。

    要在 Egg.js 中使用 redis,只需要执行 npm i redis 命令即可。

    通过一个例子来验证一下,创建一个可以返回开源仓库在 Github 上的 star 数量的数据接口,来测试使用 Redis 后带来的提升,代码地址: https://github.com/xrr2016/egg-redis-test

    开发

    首先使用 Egg.js 创建一个项目

    1
    2
    3
    4
    5
    mkdir egg-redis-test && cd egg-redis-test

    npm init egg --type=simple

    npm i

    安装 redis

    1
    npm i redis

    启动项目

    1
    2
    3
    npm run dev

    open http://localhost:7001

    先创建 controller 和 service 目录,用来放处理请求和返回数据的方法,完成后的项目目录为

    folder

    修改 router.js 文件,添加路由,然后在 controller/home.js 文件实现 stars 方法

    1
    2
    3
    4
    5
    6
    7
    8
    'use strict'

    module.exports = app => {
    const { router, controller } = app

    router.get('/', controller.home.index)
    router.get('/stars', controller.home.stars)
    }

    修改 controller/home.js 文件,接收请求传过来的 query 参数向下传给 stars service 返回结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    'use strict'

    const Controller = require('egg').Controller

    class HomeController extends Controller {
    async stars() {
    const { ctx, service } = this
    const { owner, name } = ctx.query

    ctx.body = await service.home.stars(owner, name)
    }
    }

    module.exports = HomeController

    在 service/home.js 里实现 stars 方法,需要做的就是通过 controller 传过来的 owner, name 参数,请求 Github 的接口,返回数据。

    需要注意的是,这里请求的是 Github 的 graphql 接口,所以首先需要在 Github 上新建一个 token,token 不能直接写在代码里面,需要将 token 放在环境变量里,否则代码提交到 Github 后会失效。

    token

    创建 token 后使用 dotenv 保存环境变量,先安装然后在项目目录创建一个 .env 文件

    1
    npm i dotenv

    token

    然后就可以使用 egg.js 自带的 crul 方法向 Github 接口发送 post 请求,代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    'use strict'

    require('dotenv').config()
    const Service = require('egg').Service

    class HomeService extends Service {
    async stars(owner, name) {
    function setResponse(name, stars) {
    return { msg: `${name} has ${stars} stars.` }
    }

    const query = `
    query {
    repository(owner: ${owner}, name: ${name}) {
    stargazers {
    totalCount
    }
    }
    }
    `

    const result = await this.ctx.curl('https://api.github.com/graphql', {
    method: 'POST',
    dataType: 'json',
    headers: {
    Authorization: `token ${process.env.TOKEN}`
    },
    data: JSON.stringify({ query }),
    timeout: 10000
    })

    const data = result.data.data

    return setResponse(name, data.repository.stargazers.totalCount)
    }
    }

    module.exports = HomeService

    使用 postman 测试一下接口

    folder

    耗时平均 1 秒左右,接下来就是使用 Redis 添加缓存,首先需要在本地安装 Redis,参考 Redis download,Mac 可以直接使用 homebrew 安装

    安装

    1
    brew install redis

    启动

    1
    redis-server /usr/local/etc/redis.conf

    进入 redis 命令行

    1
    redis-cli

    缓存的主要逻辑就是,第一次请求完得到 Github 的数据将数据放到缓存中,再次请求的时候直接使用缓存中的数据,也需要给缓存设置一个过期时间,
    需要从环境变量中拿到 token,post 请求的数据要用 JSON.stringify 方法传给 Github 接口,否则出现解析错误,代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    'use strict'

    require('dotenv').config()
    const redis = require('redis')
    const { promisify } = require('util')
    const Service = require('egg').Service

    const REDIS_PORT = process.env.PORT || 6379
    const client = redis.createClient(REDIS_PORT)
    const getAsync = promisify(client.get).bind(client)
    const setexAsync = promisify(client.setex).bind(client)

    class HomeService extends Service {
    async stars(owner, name) {
    const key = `${owner}/${name}`
    const stars = await getAsync(key)

    function setResponse(name, stars) {
    return { msg: `${name} has ${stars} stars.` }
    }

    if (stars !== null) {
    return setResponse(name, stars)
    }

    const query = `
    query {
    repository(owner: ${owner}, name: ${name}) {
    stargazers {
    totalCount
    }
    }
    }
    `

    const result = await this.ctx.curl('https://api.github.com/graphql', {
    method: 'POST',
    dataType: 'json',
    headers: {
    Authorization: `token ${process.env.TOKEN}`
    },
    data: JSON.stringify({ query }),
    timeout: 10000
    })

    const data = result.data.data

    await setexAsync(key, 10, data.repository.stargazers.totalCount)
    return setResponse(name, data.repository.stargazers.totalCount)
    }
    }

    module.exports = HomeService

    再次测试,首先把 Redis 里面的缓存清空,使用 Redis 的命令行运行

    1
    FLUSHALL

    发送请求,第一次的耗时还是一秒多,然后在失效时间内请求,可以看到使用缓存的数据后耗时大大减少了,性能提升效果显著,实际项目可以设置一个较长的缓存失效时间

    folder

    当然缓存过期后又要重新向 Github 发送请求了,因为 Redis 已经把数据删除了

    folder

    参考

    Redis documentation

    Node Redis



沪ICP备19023445号-2号
友情链接