技术咨询、项目合作、广告投放、简历咨询、技术文档下载 点击这里 联系博主

# redis在node中的使用

在编写【日程安排吧】 (opens new window)微信小程序中有使用到 redis 进行缓存,现简单介绍一下在 nodejs 中如何使用 redis,以及如何对 redis 的读取进行简单的封装。

# 环境准备

在 node 中使用 redis 的前提是您的主机装有 redis;其安装方式如下:

  • centos 安装 redis
# 安装Redis
sudo yum -y install redis
# 启动redis
sudo systemctl start redis
# 给Redis设置密码,取消注释如下这一行
#requirepass foobared
requirepass 密码

# 重启
sudo systemctl restart redis


# 其他
systemctl start redis.service #启动redis服务器

systemctl stop redis.service #停止redis服务器

systemctl restart redis.service #重新启动redis服务器

systemctl status redis.service #获取redis服务器的运行状态

systemctl enable redis.service #开机启动redis服务器

systemctl disable redis.service #开机禁用redis服务器


  • mac 安装 redis
# 安装
brew install redis

# 1. 启动redis
brew services start redis

# 2.关闭redis服务

brew services stop redis
# 3.重启redis服务

brew services restart redis

# redis的配合文件地址: /usr/local/etc/redis.conf
vim /usr/local/etc/redis.conf

# 修改密码
requirepass 密码
# 重启
brew services restart redis

安装完成 redis 后,您就可以在项目工程下运行如下命令:

npm install redis
# 如果有typescript的同学,可以安装如下依赖
npm install --save-dev @types/redis

# 对 redis 的简单封装

我们先说说简单的使用吧,在 nodejs 中使用 redis 其实是比较简单的,创建一个 client,然后进行读取和写入的操作;

import * as Redis from "redis";
const client = Redis.createClient({
  host: "localhost",
  port: 6379,
});

//连接错误处理
client.on("error", (err) => {
  console.log("redis connect err", err);
});

client.on("connect", () => {
  console.info("redis connect success");
});
// 一个简单的数据存储
client.set("travel:i-wants", "{a:1}", function(err, result) {});
client.get("travel:i-wants", function(err, result) {
  console.log(result);
});

通过如上的代码我们发现如下几个问题:

  1. redis npm 版本,读取和存储使用的是回调函数方式; 【解决办法】我们可以转换成 promise 的形式;
  2. 对数据的存取每次都需要分别处理错误和正常的情况; 【解决办法】统一处理错误和数据封装
  3. 每次对存取数据需要写一堆 key【解决办法】key 统一提炼为常量

改造之后的代码:

import * as Redis from "redis";
const client = Redis.createClient({
  host: "localhost",
  port: 6379,
});

//连接错误处理
client.on("error", (err) => {
  console.log("redis connect err", err);
});

client.on("connect", () => {
  console.info("redis connect success");
});

type RedisHelperBase = {
  setData: (key: string, value: any, expire?: number) => Promise<any>;
  getData: (key: string) => Promise<any>;
};
//  统一处理promise的正确和错误,使用时 直接这样使用即可const [error,data] = await xxxx(); 摆脱.then .catch
function awaitWrap<T, U = any>(
  promise: Promise<T>
): Promise<[U | null, T | null]> {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, null]>((err) => [err, null]);
}
// 统一封装redis数据的存和取
const redisHelper: RedisHelperBase = {
  /**
   *
   * @param key key
   * @param value 值
   * @param expire 默认为5分钟
   */
  setData: (key, value, expire = 60 * 5) => {
    return awaitWrap( // 外层统一处理promise的then和.catch情况
      new Promise((resolve, reject) => {
        // 我这里存储的数据为string,如果是对象类型则进行转换,其实这里使用typeof value === "object" 判断也不完全正确
        client.set(
          key,
          typeof value === "object" ? JSON.stringify(value) : value,
          function (err, result) {
            if (err) {
              reject(err);
            }
            // 如果传递过期时间,那么就对指定的key的数据设置过期
            if (!isNaN(expire) && expire > 0) {
              client.expire(key, expire);
            }
            resolve(result);
          }
        );
      })
    );
  },
  getData: (key) => {
    return awaitWrap( // 外层统一处理promise的then和.catch情况
      new Promise((resolve, reject) => {
        client.get(key, function (err, result) {
          if (err) {
            reject(err);
          }
          if (result) {
            try {
              // 对数据进行解析,如果是非object的话,就直接返回内容,否则进行数据解析为Object
              resolve(JSON.parse(result));
            } catch (error) {
              resolve(result);
            }
          } else {
            resolve(result);
          }
        });
      })
    );
  },
};

export { redisHelper };

export default redisHelper;

export * from "./const";

# 在实际业务中使用

上述我们已经简单封装了 redis 的存储,下面简单介绍一下在业务中如何使用:

【日程安排吧】 (opens new window)小程序中的【想要的功能】查询和删除为例:

// 数据的查询
export async function query(ctx: BaseContext<{ status?: string }>) {
  // ...请求的参数
  // 先从redis中读取,分为两个参数,第一个参数为是否error,第二个为具体的内容
  const [error, data] = await redisHelper.getData(wantRedisKey(status));
  // 如果报错或者有没有数据就查询一下数据库
  if (error || (!error && !data)) {
    const result = await sequelize.transaction(async () => {
      return await WantsModel.findAll({
        // ...
      });
    });
    responseSuccess(ctx, result);
    // 数据同步到redis中
    await redisHelper.setData(wantRedisKey(status), result);
  } else {
    responseSuccess(ctx, data);
  }
}

// 数据的删除
export async function deleteItem(
  ctx: BaseContext<{ status?: string, id?: string }>
) {
  // ...请求的参数
  // 先从缓存中读取
  const [error, wantList] = await redisHelper.getData(wantRedisKey());
  let hasRedisDelete = 0;
  // 删除的时候 删除缓存内的数据,提前将缓存内的数据返回给用户
  if (!error && wantList && Array.isArray(wantList)) {
    const index = wantList.findIndex((ele: any) => ele.id === id);
    if (index !== -1) {
      responseSuccess(ctx, wantList[index]);
      wantList.splice(index, 1);
      hasRedisDelete = 1;
      // 更新数据
      await redisHelper.setData(wantRedisKey(), wantList);
    }
  }

  const result = await sequelize.transaction(async () => {
    return await WantsModel.findOne({
      where: {
        // ....
      },
    });
  });
  if (result !== null) {
    // 此处也可以设计为软删除,我这边这个直接是硬删除了
    result.destroy();
    // 没有使用redis的情况,直接返回数据
    !hasRedisDelete && responseSuccess(ctx, result);
  } else {
    responseError(ctx, "数据删除失败,没有查询到此数据");
  }
}

至此一个简单的 redis 在 Nodejs 中使用已经介绍完毕,至于 redis 其他的高级用法详情可见 github: redis (opens new window)

【未经作者允许禁止转载】 Last Updated: 2/4/2024, 6:06:40 AM