在 Egg.js 中使用 Redis 缓存提升性能
最后更新:
阅读时间: 5
min
前言
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 2 3
| npm run dev
open http://localhost:7001
|
先创建 controller 和 service 目录,用来放处理请求和返回数据的方法,完成后的项目目录为
修改 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 后使用 dotenv 保存环境变量,先安装然后在项目目录创建一个 .env 文件
然后就可以使用 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 测试一下接口
耗时平均 1 秒左右,接下来就是使用 Redis 添加缓存,首先需要在本地安装 Redis,参考 Redis download,Mac 可以直接使用 homebrew 安装
安装
启动
1
| redis-server /usr/local/etc/redis.conf
|
进入 redis 命令行
缓存的主要逻辑就是,第一次请求完得到 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 的命令行运行
发送请求,第一次的耗时还是一秒多,然后在失效时间内请求,可以看到使用缓存的数据后耗时大大减少了,性能提升效果显著,实际项目可以设置一个较长的缓存失效时间
当然缓存过期后又要重新向 Github 发送请求了,因为 Redis 已经把数据删除了
参考
Redis documentation
Node Redis