还在纠结 node 服务端缓存怎么做?来试试 Redis合理使用缓存,可以大大提升服务端的性能,加快请求处理效率。那 n
前言
大家好!我是 嘟老板。合理使用缓存,可以大大提升服务端的性能,加快请求处理效率。那 node 服务端缓存应该怎么做呢?如何集成相关工具呢?今天我们就在 ZimuAdmin 中集成老牌缓存工具 - Redis。
阅读本文您将收获:
- node 服务端集成 Redis 详细过程。
- Redis 常用 api 介绍。
- Redis 在 node 项目中的简单应用案例。
集成 Redis
本文基于 ZimuAdmin 项目,使用 pnpm 作为工具包管理工具。
安装依赖
需要安装以下工具包:
- node-redis:高性能的 nodejs 的 Redis 客户端工具。
- redis-om:用于将 Redis 数据结构映射成 js 对象。
终端输入以下命令,回车执行安装:
pnpm add redis redis-om -S
初始化 Redis
在初始化 Redis 前,需要先安装 Redis 环境并启动,若没有线上环境,可根据本机系统(Windows/macOS/Linux)去网上搜索,大把的教程。
src 目录下创建 redis.ts,写入以下代码:
import { createClient } from 'redis'
// redis 客户端实例
let redisInstance: any = null
// 初始化 redis
export function initRedis() {
const redis = getRedisInstance()
redis.on('error', (err: any) => console.log('Redis Client Error: ', err))
redis.on('connect', () => console.log('Redis Client connected'))
// 连接 redis
redis.connect()
}
// 获取 redis 客户端实例,单例模式
export function getRedisInstance() {
if (!redisInstance) redisInstance = createClient()
return redisInstance
}
redis.ts 导出两个函数:
- initRedis:用于初始化并连接 redis 客户端;
- getRedisInstance:用于获取 redis 客户端实例,后续调用 redis api 会用到。
在应用入口文件 app.ts 中引入 redis.ts 导出的 initRedis 函数,并调用:
import { initRedis } from './redis'
initRedis()
到此,Redis 初始化就算完成了,控制台输入 pnpm server:start
启动服务:
打印以上日志即表示已连接 Redis。
可以发现,初始化逻辑中核心是使用 createClient api 创建 redis 客户端实例,简单说说这个 api。
createClient 接收一个可缺省的对象参数,即 redis 配置项对象,若未传递,默认连接本地环境(localhost:6379)。
常用配置项如下:
- url:Redis 连接地址,格式
redis[s]://[[username][:password]@][host][:port][/db-number]
。 - socket:Socket 连接配置项。
- socket.port: Socket 服务端口,默认 6379。
- socket.host: Socket 服务主机地址,默认 localhost。
- ...
应用 Redis
常用 api 介绍
简单介绍几个比较常用的数据操作 api,更多请移步官方文档。
-
client.set:设置字符串类型的值。
语法:
client.set(key, value, [options])
。其中 options 为可选的配置项参数,包含以下配置:
EX
: 单位 秒,正整数,用于指定过期时间。PX
: 单位 毫秒,正整数,用于指定过期时间。EXAT
:单位 秒 的 时间戳,正整数,以 Unix 格式设置过期时间。PXAT
:单位 毫秒 的 时间戳,正整数,以 Unix 格式设置过期时间。NX
:布尔值,当 key 不存在时,才执行 SET 操作。XX
:布尔值,当 key 存在时,才执行 SET 操作。KEEPTTL
:布尔值,保留 key 的现有过期时间,更新缓存数据但不更新已设置的过期时间。GET
:布尔值,返回 key 原本存储的字符串值,若原值不是字符串,会报错。
eg:
client.set('key', 'value', { EX: 60, GET: true })
-
client.get:返回指定 key 对应的值。
语法:
client.get(key)
。eg:
client.get('key', 'value')
-
client.del:删除指定 key 对应的值。
语法:
client.del(key | [key1, key2 ...])
。参数接受一个字符串或字符串数组,表示删除指定一个或多个 key 对应的值。
eg:
client.del('key') client.del(['key1', 'key2'])
-
client.hSet:以 hash 数据结构存储指定字段的值。
语法:
client.hSet(key, field, value [, field, value] ...)
。可在指定 key 下存储多组 field-value。若指定的 key 在 redis 存储库中不存在,则会新建一个 hash 数据结构。
eg:
client.hSet('hash', 'field1', 'value1', 'field2', 'value2')
-
client.hGet:返回 hash 数据结构中指定字段的值。
语法:
client.hGet(key, field)
。eg:
client.hGet('hash', 'field1')
-
client.hDel:删除 hash 结构中指定字段的值。
语法:
client.hDel(key, field | [field1, field2 ...])
。参数接受一个字符串或字符串数组,表示删除指定一个或多个 field 对应的值。
eg:
client.hDel('hash', 'field1') client.hDel('hash', ['field1', 'field2'])
项目应用
我们 ZimuAdmin 中应用一下。有这样一个需求,用户在登录过程中,限定密码错误次数最多为 5 次,一旦 5 次全部输错,则锁定账号,短时间内不允许再次登录。
我们将密码错误次数记录在 redis 中,编写如下记录函数:
const PWD_ERROR_TIMES_HASH = 'PWD_ERROR_TIMES'
/**
* 记录密码错误次数
* @param username 用户名
*/
async function recordPwdErrorTimes(username: string) {
// 获取 redis 客户端实例
const client = getRedisInstance()
// 获取当前用于已输入错误次数,默认为 0
let errorTimes = (await client.hGet(PWD_ERROR_TIMES_HASH, username)) ?? 0
// 错误次数加 1
errorTimes++
// 更新 redis 中该用户密码错误次数数据
await client.hSet(PWD_ERROR_TIMES_HASH, { [username]: errorTimes })
}
逻辑比较简单:
- 调用 getRedisInstance 函数获取 redis 客户端实例;
- 调用 client.hGet 获取当前用户已有的错误次数,若无则初始为 0;
- 错误次数加 1;
- 通过调用 client.hSet 函数将新的错误次数更新到 redis。
需要注意的是,错误次数是对应到每一个账号的,所以这里使用 hash 数据结构存储。PWD_ERROR_TIMES 作 key,登录账号 作 field(字段),错误次数 即为每个账号 field 对应的值。
在 login 接口中,若检测到用户密码输入错误,则调用 recordPwdErrorTimes 函数,更新账号错误次数。
/**
* 登录接口
* @param body 请求体
*/
@Post('/login')
async login(@Body() body: any) {
// 其他验证逻辑...
// 验证用户名和密码是否正确
const isPwdSame = await comparePassword(password, user.password)
if (isPwdSame) {
// 生成 token 逻辑...
} else {
recordPwdErrorTimes(username)
return error('密码错误,请检查后重试')
}
}
}
结语
本文重点介绍了 ZimuAdmin 中集成 Redis 的详细流程,及操作 Redis 的常用方法,旨在帮助同学们加深对于 Node 服务端缓存的应用理解。希望对您有所帮助。相关代码已上传至 GitHub,欢迎 star。
如您对文章内容有任何疑问或想深入讨论,欢迎评论区留下您的问题和见解。
技术简而不凡,创新生生不息。我是 嘟老板,咱们下期再会。
往期推荐
转载自:https://juejin.cn/post/7391737135135965203