cache-invalidation.md 2.91 KB

元数据变更后的缓存失效

当 PM 在后台保存变更(给表单加列、更新权限、注册新模块)时,每个运行节点都必须丢弃对旧元数据的缓存解释。xly 通过 JMS 完成,而不是轮询。

路径

PM 在**后台**保存
    ↓
**后台**控制器写入变更的 gds_* 行
    ↓
Controller 发布 JMS “module changed” 消息
    ↓
每个节点的 xlyErpJmsConsumer 收到消息
    ↓
ConsumerChangeGdsModuleThread.run() 清除相关 Redis key
    ↓
任意节点下一次 /business/getModelBysId 调用重新读表,
并用新值重新填充缓存

处理器位于 xlyErpJmsConsumer/src/main/java/com/xly/xlyerpjmsconsumer/thread/ConsumerChangeGdsModuleThread.java

为什么是 JMS,不是轮询后清除

xly 经常跨多个节点运行(xlyEntry、xlyApi、xlyInterface 各自 JVM,有时水平扩展)。轮询“元数据是否变了”要么很慢(直到下一次轮询才可见),要么很吵(持续心跳)。JMS 可以在毫秒级把失效广播到每个节点。

代码库同时使用 ActiveMQRocketMQ,但这里记录的元数据变更路径是 ActiveMQ / JMSxlyErpJmsConsumer@JmsListener 监听 P2pQueue.ERP_JMS_ACTIVEMQ_CHANGE_GDS_MODULEConsumerChangeGdsModuleThread 负责清缓存。RocketMQServiceImpl 用于其他集成流。

会清哪些 key

Redis 缓存包含:

  • sId 的模块元数据。
  • sId 的表单元数据。
  • 按表单 sId 的字段从表列表。
  • 每租户覆盖合并结果(派生缓存)。
  • 每(模块、角色)的权限规则。

consumer thread 收到变更行 ID 后,会清掉每个可能包含它的缓存 key 家族。这里过度失效是安全选择:下一次请求多读一次 DB 的成本,远小于继续服务陈旧元数据的成本。

直接用 SQL 修改元数据时

通过 MyBatis 或通过后台做的 insert / update 会触发 JMS 事件。工程师直接在生产 DB 执行 UPDATE gdsmodule SET ... 不会触发。缓存会持续提供旧元数据,直到:

  1. 缓存 TTL 过期(实际 TTL 看缓存配置)。
  2. 重启应用服务器。
  3. 手工发送 JMS 消息(见 xlyBusinessService 中的 BusinessCleanRedisDataImpl)。

第三种是受支持的 workaround;第二种是粗暴 fallback。

常见 bug:问题其实是缓存

当现象像是“我改了但页面还是旧值”时,按顺序检查:

  1. 变更是否实际提交?(对 DB 执行 SELECT 确认。)
  2. 后台节点能否访问 JMS broker?(不能访问时,失效事件不会发布。)
  3. 所有 consumer 节点是否在运行?(暂停的节点会继续服务旧元数据直到重启。)
  4. 变更是否通过原始 SQL 完成?(那就没有 JMS 事件,需要手工触发。)

切片 1 的“五表读取”会从缓存重新运行;理解哪一层陈旧是定位 bug 的关键。