刘光辉
昨天 bb638871a7fb692d80f1b7a758f991dc0879002c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# config/shared/mq.yaml —— 消息中间件配置(2026-08-10 起为空配置,保留文件)
#
# ══ 为什么是空的 ══
# 事件总线已由 RocketMQ 切到 Redis pub/sub,见 config/shared/system-config.yaml 的
# `event.event-publish-type: redis`。切换后:
#   - `defMsgToptic` / `ssoEventReceiver` 这两个 @Bean 位于 jnpf-common-mq 的
#     MqAutoConfiguration$MqExistsdConfiguration,其 @ConditionalOnProperty 要求
#     event-publish-type=mq,**redis 档下不再注册**;
#   - 于是原来的 `spring.cloud.function.definition` 与 `spring.cloud.stream.bindings`
#     全部指向不存在的函数。Spring Cloud Stream 4.x 对缺失 Bean 只 WARN 跳过不报错,
#     但**留着会让 RocketMQ binder 照样去连 namesrv**(消费者绑定先于函数解析建立),
#     在没有 MQ 容器的环境里就是启动期无谓重连 + 日志噪音。故整体清空。
#
# ══ 为什么不删这个文件 ══
# 14 个服务的 application.yml 都把它列在 `spring.config.import` 里,且是
# **非 optional 的 `file:`**(补丁 #8 的刻意设计:缺文件启动即失败,防静默半配置)。
# 删文件等于要同时改 14 处 import 列表,收益为零、回滚面变大。保留空文件是最小改动。
#
# ══ 要改回 RocketMQ 怎么办 ══
# `git log --oneline -- config/shared/mq.yaml` 找到本次提交,revert 即可拿回完整的
# binder 配置(含 RabbitMQ / Kafka 的原厂注释样例);同时把 system-config.yaml 的
# event-publish-type 改回 mq、并恢复 docker-compose.yml 里的三个 mq 服务。
#
# ══ 为什么还要显式写一个空的 function.definition ══
# 光清空 bindings **不够**。jnpf-common-mq 的 MqAutoConfiguration$MqExistsdConfiguration 里
# 三个 @Bean 的条件并不一致(2026-08-10 javap 实查,属 JNPF 上游的不一致):
#   defMsgToptic          @Bean + @ConditionalOnProperty(event-publish-type=mq)  ← 受门控
#   ProjectEventMQSender  @Bean + @ConditionalOnProperty(event-publish-type=mq)  ← 受门控
#   ssoEventReceiver      @Bean + **仅 @ConditionalOnMissingBean**               ← 不受门控!
# 于是 redis 档下 ssoEventReceiver 这个 Consumer<Message<T>> 照样注册(类级条件只要求
# StreamBridge 在 classpath 上)。而 Spring Cloud Stream 4.x 在 function.definition 未设时
# 会**自动发现**函数式 bean 并为其建立入站绑定 → 去连 name-server(已被清空)→
# 实测每 30s 一次 `DefaultMQPushConsumer init failed ... connect to null failed`。
# 两条走不通的弯路(2026-08-10 都实测踩过,别再试):
#   ① `definition: ""` —— 空串被当成「未设置」,SCS 照样回退到自动发现。现象很有迷惑性:
#      cx-audit 干净而 cx-lims/visualdev/system/oauth 每 30s 报错一次,因为 audit 当时有两个
#      函数式 bean(ssoEventReceiver + auditEventReceiver),自动发现遇歧义会放弃;
#      其余服务只有 ssoEventReceiver 一个,正好被自动绑上。
#      (2026-08-11 起 auditEventReceiver 已随 MQ 消费者一并删除,audit 也只剩 ssoEventReceiver
#      一个——**这条豁免不再成立**,autodetect: false 对它同样是必需的,别按上面的现象推断。)
#   ② `definition: <不存在的函数名>` —— 更糟:SCS **不会**因为函数 bean 不存在就跳过入站绑定,
#      而是照样按这个名字建 `<占位名>-in-0` 绑定去连 broker。实测日志里出现
#      `errorChannel '….eventBusDisabledPlaceholder-in-0.errors'`,报错照旧。
#      (jnpf-audit.yaml 原注释里「缺失 Bean 的项框架 WARN 跳过」在入站绑定场景不成立。)
#
# 正解是关掉自动发现这件事本身:StreamFunctionProperties.autodetect。
spring:
  cloud:
    stream:
      function:
        autodetect: false
 
# ⚠️ 审计事件(原 AUDIT_EVENT topic)与本文件已无关系:
# 2026-08-11 起 AuditEventPublisher 的第一级 MQ(streamBridge.send("AUDIT_EVENT", …))
# 与服务端消费者 auditEventReceiver 一并移除,降级链现为 **Feign 同步 → 本地 spool 落盘** 两级。
# 移除理由:切 redis 后第一级每次必然失败再由 Feign 接管,它已不是降级级次、只是每条事件
# 多一次异常构造。仍在 audit-sender 守护线程池内执行,不阻塞业务线程。
# 详见 jnpf-biz-common/jnpf-audit-sdk/.../AuditEventPublisher.java。