MST
星途 面试题库

面试题:Node.js 高并发场景下异步任务取消与超时控制的优化策略

在一个Node.js的高并发Web应用中,存在大量同时发起的异步任务,每个任务都需要设置超时控制和可取消功能。现有的实现方式在高并发时性能下降明显。请分析可能导致性能下降的原因,并提出至少两种优化策略,阐述每种策略的原理、实现要点以及在实际应用中的优缺点。
36.6万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

性能下降可能原因

  1. 资源竞争:大量异步任务同时请求资源(如数据库连接、文件系统访问等),导致资源竞争,任务等待时间延长。
  2. 事件循环阻塞:超时控制和取消功能实现不当,可能导致事件循环阻塞。例如,频繁的定时器检查或复杂的取消逻辑处理。
  3. 内存开销:每个任务都设置超时和取消功能,可能会创建大量的定时器和状态管理对象,增加内存开销,导致垃圾回收压力增大。

优化策略一:使用Promise.raceAbortController

  • 原理Promise.race可以在多个Promise中只要有一个完成就返回,结合AbortController用于取消异步操作。通过这种方式可以更高效地管理超时和取消逻辑。
  • 实现要点
    • 创建AbortController实例,获取signal
    • 将异步任务封装为Promise,并在任务内部监听signalabort事件,若事件触发则抛出异常。
    • 使用Promise.race将任务Promise和超时Promise进行竞争。
const { AbortController } = require('node:abort_controller');

function asyncTask(signal) {
    return new Promise((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            resolve('Task completed');
        }, 1000);

        signal.addEventListener('abort', () => {
            clearTimeout(timeoutId);
            reject(new Error('Task aborted'));
        });
    });
}

const controller = new AbortController();
const { signal } = controller;

Promise.race([
    asyncTask(signal),
    new Promise((_, reject) => {
        setTimeout(() => {
            reject(new Error('Task timed out'));
        }, 500);
    })
]).then(console.log).catch(console.error);

// 取消任务
controller.abort();
  • 优点:实现相对简单,利用原生的PromiseAbortController,性能较好,能有效管理超时和取消。
  • 缺点:对于复杂的异步任务,需要仔细处理内部逻辑以响应signalabort事件。

优化策略二:利用队列和限流

  • 原理:将异步任务放入队列中,通过限流控制同一时间执行的任务数量,减少资源竞争和事件循环阻塞。同时在队列任务执行时设置超时和取消逻辑。
  • 实现要点
    • 创建任务队列和一个计数器记录正在执行的任务数量。
    • 利用async/await控制任务按顺序从队列中取出执行。
    • 为每个任务设置超时和取消逻辑。
class TaskQueue {
    constructor(concurrency) {
        this.queue = [];
        this.running = 0;
        this.concurrency = concurrency;
    }

    enqueue(task, timeout, controller) {
        return new Promise((resolve, reject) => {
            this.queue.push({ task, timeout, controller, resolve, reject });
            this.processQueue();
        });
    }

    async processQueue() {
        while (this.running < this.concurrency && this.queue.length > 0) {
            const { task, timeout, controller, resolve, reject } = this.queue.shift();
            this.running++;
            const timeoutId = setTimeout(() => {
                controller.abort();
                reject(new Error('Task timed out'));
            }, timeout);
            try {
                const result = await task(controller.signal);
                clearTimeout(timeoutId);
                resolve(result);
            } catch (error) {
                clearTimeout(timeoutId);
                reject(error);
            } finally {
                this.running--;
                this.processQueue();
            }
        }
    }
}

// 使用示例
const queue = new TaskQueue(5);
const controller = new AbortController();
queue.enqueue(() => asyncTask(controller.signal), 1000, controller)
  .then(console.log).catch(console.error);
  • 优点:有效控制资源竞争,提高系统稳定性,适用于高并发场景。可以灵活调整并发任务数量。
  • 缺点:实现相对复杂,需要管理任务队列和并发控制逻辑。如果并发数设置不当,可能影响整体性能。