外观
yidun-message — 易盾消息中心(邮件/短信/PoPo 统一鉴权下发)
分层:业务支撑层 | 部署单元:2 个可构建(另 3 个有启动类不构建)| 数据库:仓库内无 DB 配置(推断由 k8s 注入)| base image:
private-registry.yidun.internal/yidun/base-image:jdk8
一、模块定位与架构
把易盾各子服务各自的“发邮件 / 发短信 / 发 PoPo”需求收敛成一套带统一鉴权(secretId/secretKey + 签名)的对外服务,避免密钥分散、模板重复、通道不可控。所有反垃圾产品的到期提醒、阈值告警、值班通知、客户余额提醒、销售通知都经此下发,是典型的“网关型”支撑服务。
架构分层
- 公共层:
common(枚举/常量/工具)、domain(MailMessage等模型) - 发送层:
service/business(MailMessageService、SmsMessageService、PopoMessageService、ServiceConfigManager)、service/base(仅占位RedisComponent) - 门面层:
facade/http-api(对外 HTTP + Kafka 生产)、facade/dubbo-provider(Dubbo 壳,无 Provider)、console/api(管理台壳) - 消费层:
consumer(消费Yidun_Message_Email,走 mfast SMTP 投递) - SDK:
sdk(DefaultYidunMessageClient对外 Java 客户端)
关键数据流
- 各子服务 →
/message/v1/*(EmailSubmitController/SmsController/PopoController),先过BaseHttpRequest鉴权链(secretId → 权限 → 参数校验 → API 版本 → MD5 签名) - 邮件异步:
EmailSubmitController.send→JdYidunKafkaProducer(topicYidun_Message_Email)→EmailConsumer.jdConsume→MailMessageService.sendMail(按@service后缀与EmailType路由defaultMailSender/serviceMailSender/oemServiceMailSender) - 邮件同步:
syncSend直连MailMessageService.sendMail - 短信:
SmsController→SmsMessageService.smsSend→ HTTPPOST ${sms.api.url}/sys/sms/send - PoPo:
PopoController→PopoMessageService.sendPopoMsg→ base64 内容 POST 企业 IM 网关 - 销售通知:先 HTTP 调
${yidun.api.url}/account/getAccountSaleManagerInfo(yidun-account)拿销售经理
二、可部署服务清单
| 部署单元目录 | artifactId | 镜像名 | 端口 | 服务类型 | 独立 Dockerfile | 是否进 kubernetes.yml |
|---|---|---|---|---|---|---|
facade/http-api | yidun-message-facade-http | private-registry.yidun.internal/yidun/yidun-message-facade-http:xinhuaxuanwang-201221 | 8909(undertow) | HTTP + Kafka Producer | 有 | 否(无 kubernetes.yml) |
consumer | yidun-message-consumer | private-registry.yidun.internal/yidun/yidun-message-consumer:1.0.0.private | 无 Web(仅 starter) | Kafka Consumer | 有 | 否(无 kubernetes.yml) |
console/api | yidun-message-console-api | 无(不构建) | 8080(undertow) | 控制台后端 | 无 | 否 |
facade/dubbo-provider | yidun-message-facade-dubbo-provider | 无(不构建) | Dubbo port=-1 | Dubbo 壳(无 Provider) | 无 | 否 |
scheduler | yidun-message-scheduler | 无(不构建) | 无 Web | 定时任务(空) | 无 | 否 |
sdk | yidun-message-sdk | — 类库,不部署 | — | 对外 Java SDK | 无 | 否 |
本仓库无
kubernetes.yml;buildAll.sh通过find . -name build.sh只发现facade/http-api与consumer两个build.sh,其余单元不会被打包。
三、同模块启动顺序
consumer—— 异步邮件的投递端,先起来可避免Yidun_Message_Email消息在 broker 侧积压无人消费。facade/http-api—— 对外提交入口,依赖同一 JD Kafka 集群与 OSS;起晚一点只会让请求短暂失败,不影响已有消息。- (可选)
console/api—— 纯控制台查询壳,按需启动。 - (不建议)
facade/dubbo-provider—— 包内无 Provider 实现,部署后“注册成功但无服务”。 - (不建议)
scheduler—— 全模块无任何任务类,部署即空跑。
理由:consumer 与 facade/http-api 仅经 Kafka topic 弱耦合,理论上可并行;但发送失败重试由 EmailConsumerRecover 兜底,先起消费端更安全。
四、逐服务启动逻辑
facade/http-api
- 启动类:
com.netease.is.yidun.message.facade.http.HttpApplication(facade/http-api/src/main/java/com/netease/is/yidun/message/facade/http/HttpApplication.java) @SpringBootApplication:无 exclude@Enable*:@EnableOssClient(com.netease.is.antispam.components.oss.configuration)- 配置类:
FacadeHttpConfiguration(@Configuration+@ComponentScan({"com.netease.is.yidun.message.service","com.netease.is.yidun.message.kafka","com.netease.is.yidun.message.configuration"}),无@MapperScan;@Bean RedisComponent;@Bean("multipartResolver") CommonsMultipartResolver且maxUploadSize=52428800(50MB));SwaggerConfiguration(@EnableSwagger2) main():仅SpringApplication.run(...)- 启动钩子:未发现
@PostConstruct/Runner/SmartLifecycle;无健康态标记(对比 export 的ServiceStatus)
consumer
- 启动类:
com.netease.is.yidun.message.consumer.ConsumerApplication @SpringBootApplication:无 exclude@Enable*:@EnableOssClient;JdKafkaConfiguration上@EnableKafka- 配置类:
ConsumerConfiguration(@ComponentScan({"com.netease.is.yidun.message.service","com.netease.is.yidun.message.configuration"}));JdKafkaConfiguration(@Bean("jdKafkaListenerContainerFactory") ConcurrentKafkaListenerContainerFactory,concurrency=8、enable.auto.commit=false、max.poll.records=50、max.partition.fetch.bytes=10485760,bootstrap 取jd.app.kafka.bootstrap.servers) main():仅SpringApplication.run(...)- 启动钩子:
YidunQosApplication(com.netease.is.yidun.message.consumer.configuration,@QosRegister implements QosApplication):doOffline()先kafkaListenerEndpointRegistry.stop(),再遍历ConcurrentMessageListenerContainer逐个stop(),最后sleep 3000ms;doOnline()仅打印日志EmailConsumer(@PostConstruct init建BoundedThreadPoolExecutor;@PreDestroy release关闭;@KafkaListener(topics=MessageConstant.YIDUN_MESSAGE_EMAIL_TOPIC, containerFactory="jdKafkaListenerContainerFactory"))、EmailConsumerRecover(失败重投兜底)
console/api(有启动类,不构建)
- 启动类:
com.netease.is.yidun.message.console.api.ConsoleApplication;@SpringBootApplication(无 exclude) - 配置类:
ConsoleConfiguration(@ComponentScan({"...service","...manager"})+@MapperScan("com.netease.is.yidun.message.dao"))、SwaggerConfiguration(@EnableSwagger2) - 端口 8080(undertow);
main()仅 run;启动钩子未发现
facade/dubbo-provider(有启动类,不构建)
- 启动类:
com.netease.is.yidun.message.facade.dubbo.DubboApplication;@SpringBootApplication+@EnableAspectJAutoProxy - 配置类:
FacadeDubboConfiguration(@ComponentScan({"...service","...manager"})+@MapperScan("...dao")+@Bean RedisComponent) dubbo.scan.basePackages=com.netease.is.yidun.message.facade.dubbo.provider,该包下不存在任何 Provider 实现(源码树只有DubboApplication/FacadeDubboConfiguration/GlobalExceptionHandler)→ 不对外暴露 Dubbo 服务main()仅 run;启动钩子未发现
scheduler(有启动类,不构建)
- 启动类:
com.netease.is.yidun.message.scheduler.SchedulerApplication;@SpringBootApplication+@EnableScheduling - 配置类:
SchedulerConfiguration(@ComponentScan+@MapperScan) - 全模块未发现
@Scheduled/@ElasticJobConf任务类,属空壳
五、启动前置依赖
| 依赖 | 配置键 / 地址 | 阻塞 or 弱依赖 | 配置文件 |
|---|---|---|---|
| Kafka(公共) | spring.kafka.bootstrap-servers,consumer group spring.kafka.consumer.group-id=yidun-message-consumer | 阻塞(consumer) | application-online.properties / application-private.properties |
| Kafka(京东独立集群) | jd.app.kafka.bootstrap.servers | 阻塞(consumer 邮件投递) | application-online.properties / application-private.properties |
| OSS / NOS | oss.client.provider=netease、oss.client.default-instance=yidun、oss.client.instance-map.yidun.endpoint=nos.netease.com | 弱(附件上传) | 各单元 application.properties |
| 邮件通道 mfast | mail.host=trans.mfast.163.com、mail.username=yidun@service.netease.com | 弱 | 各单元 application.properties |
| 短信平台 | sms.api.url(online https://message.dun.163.com) | 弱 | application-online.properties |
| 账号平台 | yidun.api.url(online http://yidun-api.service.163.org) | 弱 | application-online.properties |
| PoPo 网关 | popo.webservice.popo.163.com | 弱 | 代码内 |
| MySQL | 仅 facade/dubbo-provider 的 online 出现占位 jdbc:mysql://127.0.0.1:3306/fate,其余单元未出现 DB 配置 | 阻塞(若部署) | facade/dubbo-provider/application-online.properties |
| ZK | 仅 facade/dubbo-provider dubbo.registry.address=127.0.0.1:2181 占位 | 阻塞(若部署) | facade/dubbo-provider/application-online.properties |
| Apollo | 未发现 Apollo 配置 | — | — |
六、启动参数与 Profile
spring.profiles.active:application.properties默认dev;仓库含dev/test/online,consumer与facade/http-api另有private- Dockerfile:
ENV JAVA_OPTS=-Xmx1024m -Xms1024m -XX:+UseG1GC -Dspring.profiles.active=private - 容器 base image 与仓库:
private-registry.yidun.internal/yidun/base-image:jdk8(JDK8),镜像仓库private-registry.yidun.internal/yidun - 构建脚本参数:
buildAll.sh固定GLOBAL_ENV=private,逐个子模块bash build.sh -c false -p false -e private;build.sh用sudo docker build+sudo docker push(非 buildx) - maven profile:
private/test/online/默认(buildAll.sh统一-Pprivate)
七、启动期踩坑
git pull回退层级不一致:facade/http-api/build.sh用cd ../..(两级),consumer/build.sh用cd ..(一级)——二者目录层级不同,直接照抄会git pull到错误目录。- 镜像 tag 硬编码:
xinhuaxuanwang-201221/1.0.0.private写死在build.sh,不改-t会覆盖旧镜像;且用docker build(单架构)而非docker buildx,无 ARM 产物。 - http-api 无健康态标记:不像 export 有
ServiceStatus.STARTED,探活只能靠端口 8909,容器 “起得来但依赖未就绪” 无法被区分。 - dubbo-provider 部署即“假可用”:
dubbo.scan.basePackages指向的包下无 Provider 实现,Dubbo 注册成功却无任何服务,消费方调用会 500/找不到服务。 - scheduler 部署后空跑:无
@Scheduled/@ElasticJobConf,长期占用一个 Pod 却不执行任务。 - profile 漏传会落到 dev:
application.properties默认spring.profiles.active=dev,且 dev 指向 test 环境地址;容器若未带-Dspring.profiles.active=private,会静默连到测试中间件。 - consumer 无 Web 端口:仅
spring-boot-starter,若按 HTTP 探针探测会始终失败。 - console/api 与 dubbo-provider 无 Dockerfile:
buildAll.sh只处理有build.sh的目录,这两个单元改代码不会产出镜像。
八、跨模块前置
- 上游(调用方):本模块是网关型服务,任意易盾子服务均可经
/message/v1/*或 Java SDK(DefaultYidunMessageClient)调用,属运行时 HTTP 契约而非 jar 依赖;授权单元由service-config.yml的gateway.apiConfigs(secretId → secretKey → permissions)控制。 - 下游(弱依赖):
yidun-account(${yidun.api.url}/account/getAccountSaleManagerInfo)、短信平台(${sms.api.url}/sys/sms/send)、PoPo 企业 IM 网关、mfast SMTP、京东云 Kafka。 - 基础设施前置(阻塞):Kafka(公共 + 京东独立集群)必须在
consumer起来前可用,否则@KafkaListener容器反复重连;OSS/NOS 不可达会拖慢附件上传。 - 无强业务模块硬前置:除 Kafka / OSS 外,不依赖其他易盾业务模块的启动。