公司的风控系统出了个诡异的 bug。用户 A 在页面上看到自己被拒绝了,但查日志发现规则里引用的是用户 B 的订单金额。两个人完全不相干,数据却串了。排查了两天,最后定位到 QLExpress 的 DefaultContext 被当成了单例 Bean,所有请求共享同一个 context 对象,并发请求下变量互相覆盖。 这种 bug 的特征很典型——低并发时一切正常,压测或者高峰期才出现,而且数据是"偶尔串"而不是"一直错"。如果你问 QA 能不能复现,他们的回答多半是"有时候能有时候不能"。这就是并发问题的经典症状。 今天聊聊怎么在规则引擎的场景下,用 ThreadLocal 把上下文彻底隔离开,再加一个清理钩子防止内存泄露。 问题怎么发生的 先还原一下事故现场。很多人在用 QLExpress 的时候,为了省事会把 DefaultContext 注入成 Spring Bean: @Component public class RuleService { // ❌ 单例 Bean!所有请求共享 private final DefaultContext<String, Object....
