MST

星途 面试题库

面试题:Node.js Express高并发下的资源管理

假设你正在开发一个基于Node.js Express的高并发Web应用,该应用需要频繁读写文件系统和访问数据库。在高并发场景下,如何有效地管理这些资源以避免性能瓶颈?请从文件系统操作优化、数据库连接池管理等方面详细阐述你的策略和具体实现思路。
16.1万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

文件系统操作优化

  1. 使用异步操作
    • 在Node.js中,fs模块提供了同步和异步两种操作方式。对于高并发Web应用,应使用异步操作,如fs.readFilefs.writeFile等。这是因为同步操作会阻塞事件循环,导致应用在文件操作期间无法处理其他请求,而异步操作不会阻塞,能让应用继续处理其他任务。
    • 示例:
    const fs = require('fs');
    const util = require('util');
    
    const readFileAsync = util.promisify(fs.readFile);
    const writeFileAsync = util.promisify(fs.writeFile);
    
    async function readAndWrite() {
        try {
            const data = await readFileAsync('input.txt', 'utf8');
            await writeFileAsync('output.txt', data);
            console.log('File read and written successfully');
        } catch (err) {
            console.error(err);
        }
    }
    
    readAndWrite();
    
  2. 缓存频繁读取的文件
    • 对于经常读取且内容变动不频繁的文件,可以在内存中缓存其内容。这样在后续请求中,优先从缓存中获取数据,减少文件系统的I/O操作。
    • 可以使用简单的JavaScript对象作为缓存,例如:
    const fileCache = {};
    async function getFileContent(filePath) {
        if (fileCache[filePath]) {
            return fileCache[filePath];
        }
        const data = await util.promisify(fs.readFile)(filePath, 'utf8');
        fileCache[filePath] = data;
        return data;
    }
    
  3. 流操作
    • 当处理大文件时,使用流(stream)进行操作可以显著提高性能。流允许逐块处理数据,而不是一次性将整个文件读入内存。
    • 例如,读取大文件并写入另一个文件:
    const fs = require('fs');
    const readableStream = fs.createReadStream('largeFile.txt');
    const writableStream = fs.createWriteStream('newLargeFile.txt');
    
    readableStream.pipe(writableStream);
    

数据库连接池管理

  1. 使用连接池库
    • 在Node.js中,对于不同的数据库有相应的连接池库。例如,对于MySQL可以使用mysql2库的连接池功能,对于PostgreSQL可以使用pg库的连接池。
    • mysql2为例:
    const mysql = require('mysql2');
    
    const pool = mysql.createPool({
        host: 'localhost',
        user: 'root',
        password: 'password',
        database: 'test',
        connectionLimit: 10 // 设置连接池中的最大连接数
    });
    
    pool.query('SELECT * FROM users', (err, results) => {
        if (err) {
            console.error(err);
            return;
        }
        console.log(results);
    });
    
  2. 合理设置连接池参数
    • 连接池大小:根据应用的并发量和数据库服务器的性能来设置合适的连接池大小。如果连接池过小,在高并发时可能会出现连接不够用的情况;如果过大,可能会耗尽数据库服务器资源。可以通过性能测试来确定最佳的连接池大小。
    • 连接超时:设置合理的连接超时时间,避免长时间等待无效连接。如果一个连接在指定时间内无法建立,应及时返回错误,避免应用一直阻塞。
  3. 连接复用与释放
    • 连接池中的连接应被复用,当一个请求处理完成后,将连接返回给连接池,而不是每次请求都创建新的连接。同时,要注意及时释放不再使用的连接,防止连接泄漏。在连接使用完毕后,调用相应的方法将连接归还给连接池,如pool.releaseConnection(connection)(不同库的方法可能略有不同)。
  4. 健康检查
    • 定期对连接池中的连接进行健康检查,确保连接的有效性。对于无效连接,及时从连接池中移除并重新创建。可以通过定时任务,定期执行简单的数据库查询(如SELECT 1)来检查连接是否正常。如果查询失败,说明连接无效,进行相应处理。