MST

星途 面试题库

面试题:Node.js中结合缓存与压缩实现高效响应

在一个高并发的Node.js应用里,既要考虑对静态资源的缓存,又要对传输的数据进行压缩来提高响应速度。请阐述你会采取的整体策略,以及如何在代码层面实现缓存与压缩的协同工作,包括如何处理缓存更新和压缩格式选择等问题。
37.2万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

整体策略

  1. 缓存策略
    • 对于静态资源,采用HTTP缓存机制。可以设置Cache - Control头,区分不同类型的静态资源设置不同的缓存时长。例如,对于长期不变的CSS、JS和图片等资源设置较长的缓存时间;对于经常变动的资源(如HTML页面)设置较短的缓存时间或者不缓存。
    • 同时,可以在应用层实现本地缓存(如使用node - cache库),对于一些不经常变化且计算成本较高的数据进行缓存,减少重复计算。
  2. 压缩策略
    • 选择合适的压缩算法,如Gzip或Brotli。一般来说,Brotli在压缩率上优于Gzip,尤其是对于文本数据,但需要考虑浏览器兼容性。
    • 对所有响应数据(包括动态生成的数据和静态资源)进行压缩,以减少传输的数据量,提高响应速度。

代码层面实现缓存与压缩协同工作

  1. 缓存实现
    • HTTP缓存:在Node.js应用中,使用Express框架举例,设置Cache - Control头:
const express = require('express');
const app = express();

// 对于静态资源设置较长缓存时间
app.use('/static', express.static('public', {
    maxAge: 31536000 // 1年,单位毫秒
}));

// 对于动态响应,根据业务设置不同缓存策略
app.get('/data', (req, res) => {
    // 设置较短缓存时间
    res.set('Cache - Control','public, max - age = 60');
    res.send('Dynamic data');
});
  • 本地缓存
const NodeCache = require('node - cache');
const myCache = new NodeCache();

app.get('/expensive - data', (req, res) => {
    const cachedData = myCache.get('expensive - data - key');
    if (cachedData) {
        res.send(cachedData);
    } else {
        // 计算昂贵数据
        const expensiveData = calculateExpensiveData();
        myCache.set('expensive - data - key', expensiveData);
        res.send(expensiveData);
    }
});
  1. 压缩实现
    • 使用Gzip压缩
const express = require('express');
const app = express();
const compression = require('compression');

app.use(compression());

app.get('/data', (req, res) => {
    res.send('Some data to be compressed');
});
  • Brotli压缩(考虑兼容性)
const express = require('express');
const app = express();
const brotli = require('iltorb');
const acceptEncoding = require('accept - encoding');

app.use((req, res, next) => {
    const encodings = acceptEncoding(req);
    if (encodings['br']) {
        res.set('Content - Encoding', 'br');
        res.attachment('filename.br');
        const compressor = brotli.createCompressor();
        res.writeHead(200, { 'Content - Type': 'application/json' });
        compressor.pipe(res);
        compressor.write(JSON.stringify({ data: 'example' }));
        compressor.end();
    } else {
        // 回退到Gzip或不压缩
        next();
    }
});

app.use(compression());

app.get('/data', (req, res) => {
    res.send('Some data to be compressed');
});

缓存更新处理

  1. HTTP缓存更新
    • 对于静态资源,通过修改文件名(如使用版本号或哈希值)来使浏览器重新请求资源,绕过缓存。例如,将styles.css改为styles.v1.css
    • 对于动态数据,在数据发生变化时,通过设置较短的缓存时间或者直接不缓存,强制浏览器重新请求数据。
  2. 本地缓存更新
    • 在数据发生变化的业务逻辑处,手动更新缓存。例如,当某个数据库记录更新时,删除或更新本地缓存中对应的键值对:
// 假设数据库更新函数
function updateDatabaseRecord(record) {
    // 执行数据库更新操作
    // 更新缓存
    myCache.del('related - data - key');
}

压缩格式选择

  1. 兼容性优先:优先检查浏览器是否支持Brotli压缩。可以通过accept - encoding库来解析请求头中的Accept - Encoding字段,判断浏览器支持的压缩格式。如果浏览器支持Brotli,优先使用Brotli;否则,使用Gzip作为 fallback。
  2. 性能考量:在支持Brotli的情况下,Brotli通常能提供更好的压缩率,尤其是对于文本数据。但如果应用主要面向较旧的浏览器版本,Gzip可能是更合适的选择,因为它有更广泛的兼容性。同时,也需要考虑服务器的计算资源,Brotli压缩和解压缩通常比Gzip更消耗CPU资源,在高并发场景下需要权衡。