likes
comments
collection
share

还在纠结 node 服务端缓存怎么做?来试试 Redis合理使用缓存,可以大大提升服务端的性能,加快请求处理效率。那 n

作者站长头像
站长
· 阅读数 57

前言

大家好!我是 嘟老板。合理使用缓存,可以大大提升服务端的性能,加快请求处理效率。那 node 服务端缓存应该怎么做呢?如何集成相关工具呢?今天我们就在 ZimuAdmin 中集成老牌缓存工具 - Redis

阅读本文您将收获:

  1. node 服务端集成 Redis 详细过程。
  2. Redis 常用 api 介绍。
  3. Redisnode 项目中的简单应用案例。

集成 Redis

本文基于 ZimuAdmin 项目,使用 pnpm 作为工具包管理工具。

安装依赖

需要安装以下工具包:

  • node-redis:高性能的 nodejsRedis 客户端工具。
  • 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 启动服务:

还在纠结 node 服务端缓存怎么做?来试试 Redis合理使用缓存,可以大大提升服务端的性能,加快请求处理效率。那 n

打印以上日志即表示已连接 Redis

可以发现,初始化逻辑中核心是使用 createClient api 创建 redis 客户端实例,简单说说这个 api

createClient 接收一个可缺省的对象参数,即 redis 配置项对象,若未传递,默认连接本地环境(localhost:6379)。

常用配置项如下:

  • urlRedis 连接地址,格式 redis[s]://[[username][:password]@][host][:port][/db-number]
  • socketSocket 连接配置项。
  • 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。若指定的 keyredis 存储库中不存在,则会新建一个 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 })
}

逻辑比较简单:

  1. 调用 getRedisInstance 函数获取 redis 客户端实例;
  2. 调用 client.hGet 获取当前用户已有的错误次数,若无则初始为 0
  3. 错误次数加 1
  4. 通过调用 client.hSet 函数将新的错误次数更新到 redis

需要注意的是,错误次数是对应到每一个账号的,所以这里使用 hash 数据结构存储。PWD_ERROR_TIMESkey登录账号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
评论
请登录