防止阻塞同步操作的事件循环

问题描述 投票:0回答:1

我工作的公司有一个微服务,在 30 个 NodeJS Pod 中每秒处理 300 个请求。

DataDog 中的指标显示今天请求达到峰值时延迟和 CPU 使用率很高。

我正在观看不同 Pod 的 DataDog APM 配置文件和跟踪, 下面的函数似乎需要花费很多时间来执行 - 2.43 秒的实际时间,占 Pod 配置文件的 43%。 高峰时段 Pod 内存约为 600mi

await asyncFunc1(items);
const asyncFunc1 = async (items: SomeInterface[]) => {
  const response = await Promise.all(
    items.map(async (item) => {
      const response2 = await asyncFunc2(item);
      return response2;
    })
  );

  return response;
};

const asyncFunc2 = async (item: SomeInterface) => {
  const key = item.key;
  const inMemoryData = getInMemoryDataSync(key); // get cached data in memory - 99% data is cached.
  if (inMemoryData) {
    return JSON.parse(inMemoryData);
  }

  const redisData = await redisRepository.getData(key);
  if (redisData) {
    addInMemoryDataSync(redisData); // add cached data in memory
    return JSON.parse(redisData);
  }

  return null;
};

该代码在调用 asyncFunc2 时使用了等待操作。

asyncFunc2 的第一部分是同步 - 获取内存数据。 99% 的请求会在内存中查找缓存数据,而不会从 Redis 中获取。 这意味着对于非异步操作应用了不必要的等待。

我在谷歌上发现的是,这不会使同步操作成为异步的,但它会因为等待而创建一个微任务。 微任务将在事件循环的下一个周期之前执行。 因此,如果每秒有 300 个请求,则 300 个微任务将优先于事件循环任务。

我想到了这样的事情 -

const preventMicroTasksInSyncOperations = async (items: SomeInterface[]) => {
  const response = [];
  const keysToFetch = [];

  items.forEach((item) => {
    const key = item.key;
    const inMemoryData = getInMemoryDataSync(key); // get cached data in memory - 99% data is cached.
    if (inMemoryData) {
      response.push(JSON.parse(inMemoryData));
    } else {
      keysToFetch.push(key);
    }
  });

  await Promise.all(
    keysToFetch.map(async (key) => {
      const redisData = await redisRepository.getData(key);
      if (redisData) {
        addInMemoryDataSync(redisData); // add cached data in memory
        response.push(JSON.parse(redisData));
      }
    })
  );

  return response;
};

await preventMicroTasksInSyncOperations(items);

您对那个版本的 PreventMicroTasksInSyncOperations 有何看法? 这样所有的同步操作就不会创建微任务,而是一一执行。

感谢您的帮助。

node.js performance asynchronous async-await scale
1个回答
0
投票

根据我对您遇到的问题的理解。在我看来,这不是一个真正的问题,一切正常。但是,它不符合您的要求。

如您所知,云服务是“即用即付”的,并且不限制您的硬件使用。这意味着它们提供了运行 300 个并发任务甚至更多任务的基础设施,但费用却更高。 我想说的是,您当前的代码并未针对

成本效益

执行进行优化,而是针对面向性能执行进行优化。此外,当您同时运行所有任务时,这意味着“我希望所有任务尽快完成”(性能)。但是,您正在寻找的是“我想运行任务但不是尽可能快”(考虑成本) 你的代码有问题

我认为你的新版本代码与旧版本没有区别。 (防止MicroTasksForSyncOperations)。因为您试图在这段代码中同时启动所有任务:

await Promise.all( keysToFetch.map(async (key) => { const redisData = await redisRepository.getData(key); if (redisData) { addInMemoryDataSync(redisData); // add cached data in memory response.push(JSON.parse(redisData)); } }) );

您可以运行所有任务,而无需等待已创建的任务完成。结果,您将看到与之前相同的结果。

© www.soinside.com 2019 - 2024. All rights reserved.