在心理测评的线上实战场景中,校园机房的网络环境有时并不稳定。当学生耗费大量时间填完一份数百题的人格量表,点击“提交答卷”后,如果遇到局域网拥堵,浏览器可能迟迟无法收到后端的成功响应。出于焦虑,学生往往会连续多次点击提交按钮。
如果前端仅仅做了基础的防抖(Debounce)甚至缺少这类防护,短时间内就会有多个相同的 HTTP POST 请求到达后端服务器。假如后端 Controller 只是简单地执行入库操作,数据库中就会产生多份完全一致的答卷记录。这种数据冗余不仅会干扰心理老师的后续诊断,还会影响按份数结算测评版权费用的准确性。
为了杜绝这种由重放请求引发的数据问题,相关的关键业务接口必须具备幂等性(Idempotency)。简单来说,幂等性保证了客户端使用相同的参数无论向接口发起 1 次还是多次请求,后端系统只处理最先到达的那 1 次,其余的重复请求均被过滤,且返回一致的结果状态。
针对“交卷”这类不允许重复落库的核心接口,橙星云技术团队引入了基于 Redis 的防重 Token(Idempotent Token)机制。整个防御链路分为三个环节:
在答卷页面初始化时,前端需要向后端请求一枚唯一的 Token(通常是随机 UUID)。后端生成后将其存入 Redis,并设置一个合适的过期时间(例如 30 分钟)。当学生点击“交卷”时,前端会在 HTTP Header(如 X-Idempotent-Token)中携带这枚 Token 发起请求。
当网络延迟导致多个重放请求几乎同时抵达后端网关时,网关层的拦截器会通过一段 Lua 脚本去 Redis 中查验并删除该 Token。得益于 Redis 单线程模型提供的原子性操作,只有并发请求中最快到达的那一个,能够成功删掉 Token 并获得放行许可,进入后续的业务逻辑。其余紧随其后的请求在查验时会发现 Token 已经不存在,网关将直接进行拦截,并返回一个提示重复提交的状态信息。
除了在网关层的内存级拦截,数据库层面的兜底防御同样必不可少。为防止 Redis 出现异常或网络穿透,我们需要在 MySQL 的相关业务表中,针对 studentid、scaleid 和 task_id 这几个核心字段建立联合唯一索引(Unique Key)。
这样一来,即使有漏网的重复请求试图第二次插入相同的数据,MySQL 底层的 InnoDB 引擎也会通过抛出 DuplicateKeyException 来拒绝执行。后端只需捕获该异常,并向客户端返回“请勿重复提交”的提示,从而在数据存储层面守住了系统的一致性边界。
