首页 > 解决方案 > nodejs中redis客户端的异步导出

问题描述

下面的代码构造一个redis客户端并导出。我正在从保管库秘密管理服务中获取 redis 密码,并且该调用是一个承诺/异步。代码不会等待该调用,它会在异步调用完成之前导出 redis 客户端。我不确定我在这里做错了什么。任何想法?

import redis from 'redis';
import bluebird from 'bluebird';
import logger from '../logger';
import srvconf from '../srvconf';
import { getVaultSecret } from '../services/vault.service';

const vaultConfig = srvconf.get('vault');

bluebird.promisifyAll(redis);

let redisUrl = '';
const maskRedisUrl = (url) => url.replace(/password=.*/, 'password=*****');
const setRedisUrl = (host, port, pw) => { 
  const pwstring = pw ? `?password=${pw}` : '';
  const url = `redis://${host}:${port}${pwstring}`;
  console.log(`Setting redis_url to '${maskRedisUrl(url)}'`);
  return url;
}

if (vaultConfig.use_vault) {
  (async () => {
    const secret = await getVaultSecret(`${vaultConfig.redis.secrets_path + vaultConfig.redis.key}`)
    redisUrl = setRedisUrl(srvconf.get('redis_host'), srvconf.get('redis_port'), secret.PASSWORD);  
  })().catch(err => console.log(err));
} else {
  if (!srvconf.get('redis_url')) {
    redisUrl = setRedisUrl(srvconf.get('redis_host'), srvconf.get('redis_port'), srvconf.get('redis_password'));;
  } else {
    redisUrl = srvconf.get('redis_url');
    console.log(`Found redis_url ${maskRedisUrl(redisUrl)}`);
  }
}

const options = redisUrl
  ? { url: redisUrl }
  : {};

const redisClient = redis.createClient(options);

redisClient.on('error', err => {
  logger.error(err);
});

export default redisClient;

标签: javascriptnode.jsredis

解决方案


问题是(async () => {...})()返回 aPromise并且您没有await在顶层使用它,因此脚本继续运行超过该行,设置options = {}并返回redisClient.

您需要的是一个顶级 await,默认情况下在Node versions >= 14.8.0. 但是,如果您的项目使用比该版本更旧的版本,则有一个解决方法,如下所示。

请注意,以下代码未经测试,因为我在本地没有相同的项目设置。

模块

import redis from "redis";
import bluebird from "bluebird";
import logger from "../logger";
import srvconf from "../srvconf";
import { getVaultSecret } from "../services/vault.service";

const vaultConfig = srvconf.get("vault");

bluebird.promisifyAll(redis);

let redisUrl = "";
let redisClient = null;

const initRedisClient = () => {
  const options = redisUrl ? { url: redisUrl } : {};

  redisClient = redis.createClient(options);

  redisClient.on("error", (err) => {
    logger.error(err);
  });
};

const maskRedisUrl = (url) => url.replace(/password=.*/, "password=*****");
const setRedisUrl = (host, port, pw) => {
  const pwstring = pw ? `?password=${pw}` : "";
  const url = `redis://${host}:${port}${pwstring}`;
  console.log(`Setting redis_url to '${maskRedisUrl(url)}'`);
  return url;
};

(async () => {
  if (vaultConfig.use_vault) {
    try {
      const secret = await getVaultSecret(
        `${vaultConfig.redis.secrets_path + vaultConfig.redis.key}`
      );
      redisUrl = setRedisUrl(
        srvconf.get("redis_host"),
        srvconf.get("redis_port"),
        secret.PASSWORD
      );
    } catch (err) {
      console.log(err);
    }
  } else {
    if (!srvconf.get("redis_url")) {
      redisUrl = setRedisUrl(
        srvconf.get("redis_host"),
        srvconf.get("redis_port"),
        srvconf.get("redis_password")
      );
    } else {
      redisUrl = srvconf.get("redis_url");
      console.log(`Found redis_url ${maskRedisUrl(redisUrl)}`);
    }
  }
  // Initialize Redis client after vault secrets are loaded
  initRedisClient();
})();

export default redisClient;

用法

在您导入和使用客户端的所有地方,您始终需要检查它是否实际初始化成功,如果没有,则抛出(并捕获)明确定义的错误。

const redisClient = require("path/to/module");
...
if (redisClient) {
  // Use it
} else {
  throw new RedisClientNotInitializedError();
}
...

推荐阅读