在高并发场景下,如果页面直接向后端发起大量请求,可能会导致服务器压力过大、请求超时,甚至引起服务器崩溃。因此,需要采取一定的策略来优化页面的并发请求,降低服务器负载,提高系统的稳定性和用户体验。本文将介绍几种常见的解决方案。

前端层面优化

1. 请求去重

在某些情况下,页面可能会多次触发相同的请求,如用户快速点击按钮、页面轮询等。可以通过 请求去重 来避免无意义的重复请求。

实现方式

使用 MapSet 记录正在进行的请求,并在请求完成后移除。

1
2
3
4
5
6
7
8
9
10
11
12
13
const pendingRequests = newMap();

asyncfunctionfetchData(url: string, options = {}) {
if (pendingRequests.has(url)) {
return pendingRequests.get(url);
}

const promise = fetch(url, options)
.finally(() => pendingRequests.delete(url));

pendingRequests.set(url, promise);
return promise;
}

2. 请求合并

如果多个请求目标相同,可以合并多个请求为一个。例如,多个用户查询不同 ID 的数据,可以合并成一个批量查询请求。

实现方式

  • 例如,某个 API 需要查询多个 ID 的数据,而前端可以短时间内发起多个请求,这时可以合并多个请求,减少服务器压力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let requestQueue: string[] = [];
let requestTimeout: NodeJS.Timeout | null = null;

function batchFetch(ids: string[]) {
return fetch(`/api/data?ids=${ids.join(",")}`).then(res => res.json());
}

function fetchWithBatching(id: string) {
return new Promise(resolve => {
requestQueue.push(id);

if (!requestTimeout) {
requestTimeout = setTimeout(() => {
batchFetch([...new Set(requestQueue)]).then(resolve);
requestQueue = [];
requestTimeout = null;
}, 50); // 50ms 内的请求会被合并
}
});
}

3. 请求节流与防抖

对于频繁触发的请求(如搜索、滚动事件),可以使用 节流(throttle)防抖(debounce) 来控制请求频率。

节流(throttle)

限制请求触发的频率,在一定时间内只允许一次请求。

1
2
3
4
5
6
7
8
9
10
function throttle(fn: Function, delay: number) {
let lastTime = 0;
return function (...args: any[]) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
fn(...args);
}
};
}

防抖(debounce)

当用户输入停止一段时间后才触发请求,适用于搜索输入框等场景。

1
2
3
4
5
6
7
function debounce(fn: Function, delay: number) {
let timer: NodeJS.Timeout;
return function (...args: any[]) {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}

后端层面优化

1. 限流

对于短时间内大量请求,后端可以通过 限流 机制进行控制,防止单个用户或 IP 过度消耗资源。

常见限流算法

  • 令牌桶算法(Token Bucket)

  • 漏桶算法(Leaky Bucket)

  • 固定窗口限流

  • 滑动窗口限流

示例(使用 express-rate-limit 实现 API 限流):

1
2
3
4
5
6
7
8
9
const rateLimit = require("express-rate-limit");

const limiter = rateLimit({
windowMs: 60 * 1000, // 1 分钟
max: 100, // 每分钟最多 100 个请求
message: "请求过多,请稍后再试",
});

app.use("/api", limiter);

2.队列化请求(使用消息队列)

当请求超过后端处理能力时,可以将请求放入 消息队列(如 RabbitMQ, Kafka, Redis 队列),后端按照能力处理,避免瞬时请求量过大。

示例(使用 Redis 队列):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const redis = require("redis");
const client = redis.createClient();

// 添加请求到队列
function addToQueue(data: any) {
client.lpush("requestQueue", JSON.stringify(data));
}

// 后端消费队列
async function processQueue() {
while (true) {
const data = await client.rpop("requestQueue");
if (data) {
handleRequest(JSON.parse(data));
}
}
}

3. 缓存(Cache)

对于短时间内重复请求的数据,可以使用 缓存,减少数据库查询,提高响应速度。

常见缓存策略

  • 浏览器缓存(HTTP 缓存)

  • CDN 缓存

  • Redis/Memcached 缓存

  • 数据库查询缓存(如 MySQL Query Cache)

示例(使用 Redis 缓存 API 请求):

1
2
3
4
5
6
7
8
9
10
11
12
13
const cache = new Map();

function getDataWithCache(key: string, fetchData: () => Promise<any>) {
if (cache.has(key)) {
return Promise.resolve(cache.get(key));
}

return fetchData().then(data => {
cache.set(key, data);
setTimeout(() => cache.delete(key), 60 * 1000); // 1 分钟后清除缓存
return data;
});
}

4. 数据库优化

对于高并发的 API 请求,数据库可能成为性能瓶颈,可以采用 数据库优化 来提升性能:

  • 索引优化:为查询频繁的字段创建索引,提高查询速度。

  • 分库分表:对大数据量的表进行拆分,减少单表查询压力。

  • 读写分离:使用主从数据库架构,读操作走从库,写操作走主库。

示例(MySQL 读写分离):

1
2
SELECT * FROM users WHERE id = 123; -- 走从库
INSERT INTO users (name, age) VALUES ('Tom', 25); -- 走主库

总结

在页面请求高并发时,可以采取 前端优化后端优化数据库优化 的综合策略:

  1. 前端优化

    • 请求去重、合并请求

    • 限制请求频率(节流/防抖)

    • 异步加载、懒加载

  2. 后端优化

    • API 限流

    • 使用消息队列处理请求

    • 缓存热点数据

  3. 数据库优化

    • 读写分离、分库分表

    • 索引优化

通过合理运用这些策略,可以有效提高系统的抗压能力,避免服务器过载,保证用户在高并发环境下仍能流畅使用应用。

希望能够帮到你!

Happy Coding!