外观
yidun-private-consume — 私有化 EDA 消息消费服务
分层:私有化/边缘集成层 | 部署单元:1 个(
storage),facade/http-api为骨架不构建 | 数据库:无 | base image:private-registry.nis.netease.com/library/base-image:jdk8(tagbase-250409)
一、模块定位与架构
模块是私有化交付里的一个轻量「消息桥」:消费企业 IT 系统(EDA)下发的登出/清理类消息,按配置过滤出目标系统与消息码,再回调 CMS 控制台接口把对应 token 清理掉。
它不依赖数据库、ZooKeeper、Redis、Kafka 或 Apollo,几乎是零中间件依赖的常驻进程,因此真正的关注点是「消息入口是否接上」以及对外回调地址是否可达。
架构分层
- 入口层:
storage(StorageApplication,常驻进程 +EdaConsumer) - 业务层:
service/business(仅BusinessService标记类,供@ComponentScan(... basePackageClasses=BusinessService.class)定位包) - 服务层:
storage/.../service/TokenProcessService(回调cmsLogoutApi + /api/login/tokenClean) - 骨架层:
facade/http-api(仅有HttpApplication,无 Controller)、common、domain、service/base(空壳)
关键数据流(设计意图)
- EDA 系统下发 JSON 消息(字段含
startSysOrCmptNo、msgCd、busiMsgBody.tokenList) EdaConsumer.consume(String token)入口- 过滤一:空 token / 非法 JSON / 反序列化失败 → 记 warn 并丢弃
- 过滤二:
startSysOrCmptNo不在eda.consumer.start-sys-or-cmpt-nos白名单、或msgCd不在eda.consumer.msg-cds白名单 → 静默忽略 - 命中后投入
QUERY_POOL线程池,遍历tokenList clientType == "0"的 token →TokenProcessService.process(token)TokenProcessServicePOSTeda.cms.logout.api + /api/login/tokenClean,code==200视为成功- 全程不落库、不发 MQ,处理结果仅记日志
现状提示:
EdaConsumer.consume()在本模块内无任何调用方(仅EdaConsumerTest直接调用),且storage/pom.xml未引入 Kafka 或任何 MQ 依赖——即消息入口尚未接线,服务能起但不消费。
二、可部署服务清单
| 部署单元目录 | artifactId | 镜像名 | 端口 | 服务类型 | 独立 Dockerfile | 是否进 kubernetes.yml/Helm |
|---|---|---|---|---|---|---|
storage | privateconsume-storage | yidun-private-consume-storage | 无(spring.main.web-application-type=none) | 常驻消费进程 | 有 | 否(仓库内无清单,外部流水线部署) |
facade/http-api | privateconsume-facade-http-api | — | 8081(server.port) | HTTP 骨架(无 Controller) | 无(不构建) | 否 |
说明:
common、domain、service/base、service/business为非部署库模块;facade/http-api目录下仅有HttpApplication一个类,无 Dockerfile、无build.sh,不产出镜像。
有 build.sh 的 1 个单元:storage/build.sh(MODEL_NAME=storage、BUILD_ENV=private、DOCKER_IMAGE_NAME=yidun-private-consume-storage);buildAll.sh(GLOBAL_ENV=private,遍历 find . -name build.sh)只命中它一个。
无 build.sh(不构建)的单元:
facade/http-api:仅HttpApplication骨架,无 Controller、无 Dockerfile、无启动脚本,不产出镜像
三、同模块启动顺序
storage— 唯一真实可运行单元,先起它;它是「消息 → CMS 回调」的唯一承载facade/http-api— 若被单独运行,仅为骨架进程,与storage无依赖关系,顺序不敏感
理由:两单元之间没有任何共享资源(无共同 DB/MQ/注册中心),storage 是核心,http-api 属未完成单元,故顺序由 storage 主导。
四、逐服务启动逻辑
storage
- 启动类:
com.netease.yidun.privateconsume.storage.StorageApplication(storage/src/main/java/.../StorageApplication.java) - 注解:
@SpringBootApplication+@EnableStorageConfiguration(自定义注解)configuration/EnableStorageConfiguration.java自身标注@Component + @Import(StorageConfiguration.class)configuration/StorageConfiguration.java:@Configuration+@ComponentScan(value="com.netease.yidun.privateconsume", basePackageClasses=BusinessService.class)
main()有自定义逻辑:ConfigurableApplicationContext context = SpringApplication.run(StorageApplication.class, args)- 新建
CountDownLatch shutdownLatch = new CountDownLatch(1) context.addApplicationListener(...)监听ContextClosedEvent,收到即countDown()- 主线程
shutdownLatch.await()阻塞保活;捕获InterruptedException时Thread.currentThread().interrupt()并context.close()
- 无业务
@Enable*注解;spring.main.web-application-type=none(不启动 Web 容器) - 启动钩子:
consume/EdaConsumer的@PostConstruct init()——打印expectedStartSystems/expectedMessageCodes/poolSize- 构造函数注入
TokenProcessService与@Value("${eda.consumer.pool-size:2}") int poolSize - 建
ThreadPoolExecutor(poolSize, poolSize, 5min, SynchronousQueue, daemon 线程工厂("EdaConsumer-%d"), CallerRunsPolicy)
- 构造函数注入
- 业务 Bean:
service/TokenProcessService(@Service)@Value("${eda.cms.logout.api:}") String cmsLogoutApihttpClient = HttpClient4Utils.createNoResponseRetryInstanceTrustSSl(100, 100, 2000, 1000, 1000, 2)process(token):token 空返回true;cmsLogoutApi空则 warn 返回false;否则 POST 并解析code
- 配置来源:
application.properties(无 Apollo,全部本地配置)
facade/http-api(骨架)
- 启动类:
com.netease.yidun.privateconsume.facade.http.HttpApplication(facade/http-api/src/main/java/.../HttpApplication.java) - 仅
@SpringBootApplication+main()直接SpringApplication.run(...);无任何@Enable*/@Import/ Controller - 无 Dockerfile、无
build.sh,属未完成骨架
五、启动前置依赖
| 依赖 | 配置键/地址 | 阻塞 or 弱依赖 | 配置文件 |
|---|---|---|---|
| 无 Apollo | 全模块无 apollo.* 配置 | — | — |
| 无 ZooKeeper | 无 Dubbo / ElasticJob 依赖 | — | — |
| 无数据库 | storage/pom.xml 无 JDBC 依赖(仅 spring-boot-starter) | — | — |
| 无 Redis | pom 无 Redis 依赖 | — | — |
| 无 Kafka | pom 无 spring-kafka / kafka-clients | — | — |
| CMS 控制台登出接口 | eda.cms.logout.api=http://antispam-cms-console-api.yidun.svc(调用 /api/login/tokenClean) | 弱依赖(为空则消息被丢弃) | application.properties |
注意:
TokenProcessService.process()中eda.cms.logout.api为空时打 warn 并return false,token 清理被静默跳过。
六、启动参数与 Profile
-Dspring.profiles.active:application.properties无spring.profiles.active(仅spring.application.name、spring.main.web-application-type=none与 3 个eda.*键);Dockerfile 设-Dspring.profiles.active=private- Maven profile(根 pom 显式定义):
default(activeByDefault,deploy.env=default)、private(deploy.env=private)——与model-evaluation不同,本模块 private 是真实存在的 profile - 构建:
buildAll.sh的GLOBAL_ENV=private,遍历find . -name build.sh(仅命中storage/build.sh) storage/build.sh:默认BUILD_ENV=private、DOCKER_IMAGE_NAME=yidun-private-consume-storage、DOCKER_IMAGE_TAG=base-250409、DOCKER_REPOSITORY=private-registry.nis.netease.com/yidunmvn clean install --pl storage -am -T 1C -U -Dmaven.test.skip=true -P privatedocker buildx build --platform linux/amd64,linux/arm64 --push后podupdate.sh
JAVA_OPTS:-Xmx1024m -Xms1024m -XX:+UseG1GC -Dspring.profiles.active=private;ENTRYPOINT ["bash","docker-entrypoint.sh"]- base image / 仓库:
private-registry.nis.netease.com/library/base-image:jdk8(JDK 8,Spring Boot 2.7.18、java.version=8);构建仓库private-registry.nis.netease.com/yidun;tagbase-250409 - 依赖栈:
spring-boot-starter(无 web)、spring-boot-starter-log4j2、general-tools-common、fastjson、commons-lang3、lombok - 日志:
log4j2.xml+log4j2.component.properties+log4j2/{console,file}-appender.xml;filter 资源storage/src/main/filter/{default,private}.properties
七、启动期踩坑
- 服务能正常起,但不消费任何消息:
EdaConsumer.consume(String)无触发源,且项目无 MQ 依赖;除非外部把consume()接上(如另建 HTTP 入口或引入 Kafka),否则该进程只是「空转保活」。 spring.main.web-application-type=none+ 手写CountDownLatch保活:若去掉shutdownLatch.await(),main返回后 JVM 立即退出;因此 k8s 不能用 HTTP 健康检查做存活探针(本单元无 HTTP 端口)。eda.cms.logout.api为空时,TokenProcessService.process()直接 warn 返回false,token 清理被丢弃且不会重试;部署前必须确认该值指向可达的antispam-cms-console-api。EdaConsumer线程池用SynchronousQueue+CallerRunsPolicy:无队列缓冲,突发消息时超出poolSize的任务会落到调用线程(消费线程)同步执行,可能阻塞消费。@EnableStorageConfiguration是自定义注解而非 Spring 内置,扫包依赖basePackageClasses=BusinessService.class;移动BusinessService包路径会直接导致组件扫描失效(@ComponentScan的 value 与 basePackageClasses 同时存在,二者任一失配都会漏扫)。TokenProcessService的HttpClient4Utils.createNoResponseRetryInstanceTrustSSl(...)默认无视 SSL 校验且不重试响应,对端异常时只能靠Calling token clean API/Failed to clean...日志排查。EdaConsumer.consume()对消息做双重过滤(startSysOrCmptNo+msgCd同时命中才处理),默认值99711660000/997116600000033与真实 EDA 消息码不一致时,所有消息都会被debug级静默丢弃——先确认配置与上游一致,再看日志级别是否含 debug。- 消息字段过滤只识别
clientType == "0"的 token,其它 clientType 会被跳过;若上游改了 clientType 编码,功能会静默失效。
八、跨模块前置
antispam-cms(或对应私有化 CMS 控制台):eda.cms.logout.api指向其antispam-cms-console-apiService,未就绪则 token 清理回调失败(弱依赖)- EDA / 企业 IT 系统:消息来源方,模块本身不主动拉取(弱依赖)
- 基础设施:无(不依赖 Apollo、ZK、MySQL、Redis、Kafka),是私有化交付中依赖最少的单元之一