set适用于客户端或网关层轻量级请求去重,以请求指纹为key判重并配合手动清理,但无法解决多实例部署、网络超时、序列化不一致等问题,需服务端幂等校验兜底。

在高并发场景下,用 Set 过滤重复请求的核心思路是:**以请求的唯一标识(如 URL + 参数摘要)为 key,用 Set 快速判重 + 自动去重,配合短期缓存或时间窗口控制生命周期**。单纯靠 Set 无法解决服务端幂等性问题,但它非常适合做客户端或网关层的轻量级请求“削峰去重”。
用 Set 缓存请求指纹,拦截重复发起
对每个请求生成一个确定性字符串(即“指纹”),例如:`${url}|${JSON.stringify(sortedParams)}` 或更稳妥的 hash(`${url}?${new URLSearchParams(params).toString()}`)。
把指纹存入全局 Set,发送前检查是否存在;存在则跳过,否则加入并发出请求。
- 适合防用户连点、自动重试、前端轮询等场景
- 注意:需搭配清理机制,否则内存泄漏(见下一点)
- 示例:
const pendingRequests = new Set();<br>
function makeUniqueRequest(url, params) {<br>
const fingerprint = `${url}|${JSON.stringify(params)}`;<br>
if (pendingRequests.has(fingerprint)) return Promise.reject('duplicate');<br>
pendingRequests.add(fingerprint);<br>
return fetch(url, { method: 'POST', body: JSON.stringify(params) })<br>
.finally(() => pendingRequests.delete(fingerprint));<br>
}
结合定时清理,避免 Set 无限增长
Set 本身无过期能力,必须手动管理生命周期。推荐两种方式:
-
请求完成即清除:如上例中用
.finally()删除,最常用且安全 -
带 TTL 的简易缓存封装:用
Map存指纹 + 时间戳,定期扫描过期项(Set不支持键值对,所以这里实际用Map更合适,但逻辑仍围绕“去重”目标)
若坚持只用 Set,可搭配 setTimeout 延迟删除(不推荐用于关键路径,有竞态风险):
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
pendingRequests.add(fingerprint);<br> setTimeout(() => pendingRequests.delete(fingerprint), 5000); // 5秒后自动清理
注意边界:Set 只管“客户端瞬时去重”,不替代服务端幂等
Set 是内存级、单实例、无持久化的,它不能解决以下问题:
- 多进程/多实例部署时,各进程
Set互不影响 → 需改用 Redis 等共享存储 - 网络超时导致请求实际已发但未收到响应,
Set已清除 → 客户端仍可能重复提交 - 参数序列化不一致(如对象属性顺序不同、undefined 被忽略)→ 指纹生成必须稳定
因此,Set 是第一道轻量防线,后端仍需基于业务 ID(如 order_id)做幂等校验。
进阶:用 WeakSet?不适用
WeakSet 只接受对象作为值,且不支持遍历和 .has() 查询原始值——它无法用于字符串指纹判重。所以过滤重复请求必须用普通 Set。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










