外观
antispam-business-text — 文本内容安全检测服务
分层:业务层 | 部署单元:9 个(k8s 实部署 5 个) | 数据库:MySQL / TiDB(库
antispam) | base image:private-registry.nis.netease.com/library/base-image:jdk17-kylin(tagbase-250409-kylin)
一、模块定位与架构
模块承担易盾「文本检测」产品的服务端职责:客户把聊天、评论、昵称等文本送检,模块在同步/异步两条链路上编排关键词、规则、名单、设备指纹、大模型等多引擎并发判定,输出命中标签与处置建议,并按业务开关决定回调、落库与计费。改动标签映射或判定顺序会直接影响客户线上处置、账单数据与人工审核链路。
架构分层
- 协议层:
facade/http-check-api(同步/异步提交与检测)、facade/http-api(回调拉取/反馈/查询)、facade/dubbo-provider+facade/dubbo-check-provider(Dubbo 服务实现)、facade/http-grammarfix-api(文本纠错侧入口) - 逻辑层:
check-common(CheckHandler/TextCheckService转发、熔断、限流)、facade/dubbo-check-provider/checker/*(各检测器)、service/business - 数据层:
service/base(MyBatis DAO + ES 封装)、storage(@KafkaConsumer消费落库/归档)、scheduler(Elastic-Job 扫库) - 公共/支撑:
common、domain、facade/{dubbo-api,dubbo-check-api,http-common}、check-common、console/api、async-consume、benchmark、file
关键数据流(同步检测 v5)
- 客户 →
http-check-api的/v5/text/check,过签名/密钥/字段长度/限流/区域准入校验 - 组装
TextCheckReq→CheckHandler.doCheck→TextCheckService按策略集走本地 Dubbo 或内网 HTTP 转发 dubbo-check-provider各 checker 并发检测 → 结果按优先级归并(取首个 label/subLabel)needStorage=true→ 发 Kafka →storage消费落 ES/归档并触发回调- 异步链路先写 Redis 缓冲池(
antispam-business-text-deal-{pid}-{tid}),再由async-consume消费异步检测 topic
二、可部署服务清单
| 部署单元目录 | artifactId | 镜像名 | 端口 | 服务类型 | 独立 Dockerfile | 是否进 kubernetes.yml |
|---|---|---|---|---|---|---|
facade/dubbo-provider | antispam-business-text-facade-dubbo-provider | antispam-business-text-dubbo-provider | 无 HTTP(Dubbo,qos-enable=false) | Dubbo Provider(查询/更新) | 有 | 是 |
facade/dubbo-check-provider | antispam-business-text-facade-dubbo-check-provider | antispam-business-text-dubbo-check-provider | 无 HTTP(Dubbo) | Dubbo Provider(检测执行) | 有 | 是 |
facade/http-check-api | antispam-business-text-facade-http-check-api | antispam-business-text-http-check-api | base 8110 / private 8080 | HTTP 同步 + 异步提交 | 有 | 是 |
facade/http-api | antispam-business-text-facade-http-api | antispam-business-text-http-api | 8080 | HTTP 回调/反馈/查询 | 有 | 是 |
storage | antispam-business-text-storage | antispam-business-text-storage | 无 HTTP(Kafka 消费) | Kafka 消费落库 | 有 | 是 |
scheduler | antispam-business-text-scheduler | antispam-business-text-scheduler | 无 HTTP(定时任务) | Elastic-Job | 有 | 否(有 Dockerfile + Apollo app.id 但未部署) |
async-consume | antispam-business-text-async-consume | antispam-business-text-async-consume | 无 HTTP(Kafka 消费) | Kafka 异步消费 | 有 | 否 |
facade/http-grammarfix-api | antispam-business-text-facade-http-grammarfix-api | — | — | HTTP 文本纠错 | 无(不构建) | 否 |
console/api | antispam-business-text-console-api | — | — | 控制台后端骨架 | 无(不构建) | 否 |
说明:
common、domain、service/base、service/business、service、facade/dubbo-api、facade/dubbo-check-api、facade/http-common、check-common、benchmark、file为非部署库模块,不参与镜像构建。
三、同模块启动顺序
facade/dubbo-check-provider— checker 检测链注册到 ZK,是其余单元引用检测能力的 Dubbo 前置facade/dubbo-provider— 数据查询/更新 Dubbo 服务,供http-api与 CMS 调用facade/http-check-api— 检测转发依赖 dubbo-check(本地 Dubbo)或内网转发目标已就绪facade/http-api— 回调/反馈入口,依赖同库 DAO 与 Dubbo 提供方storage— 消费 Kafka 落库;需 Kafka/Redis/DB 就绪,否则位点无法提交scheduler— Elastic-Job 需要 ZooKeeper 与 DB;任务多为失败恢复/归档清理,晚于 storage 更安全
理由:Dubbo 提供方必须先于消费方注册;storage/async-consume 会消费 http-check-api 写入的 topic,Kafka 与 Redis 缓冲池 key 都需先可用。
四、逐服务启动逻辑
facade/dubbo-check-provider
- 启动类:
com.netease.is.antispam.text.facade.dubbo.check.CheckDubboApplication(facade/dubbo-check-provider/src/main/java/.../CheckDubboApplication.java) @SpringBootApplicationexclude:MultipleElasticsearchAutoConfiguration@Enable*:@EnableAspectJAutoProxy、@EnableTransactionManagement、@EnableBusinessClient(无自定义 AutoConfiguration 间接层)@ComponentScan({"com.netease.is.antispam.text"});无@MapperScan- 拉起组件:Dubbo Provider(checker 服务)、ZooKeeper 注册中心、business client 缓存
main():无自定义逻辑- 启动钩子:
listener/ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent>, Ordered - Apollo:
app.id=antispam-text_dubbo-check
facade/dubbo-provider
- 启动类:
com.netease.is.antispam.text.facade.dubbo.DubboApplication(facade/dubbo-provider/.../DubboApplication.java) - exclude:
DataSourceAutoConfiguration(仍保留@MapperScan,由 MyBatis 自行装配数据源) @Enable*:@EnableAspectJAutoProxy(proxyTargetClass=true)、@EnableTransactionManagement、@EnableRateLimiter、@EnableAsync、@EnableBusinessClient@ComponentScan({"com.netease.is.antispam.text","com.netease.is.antispam.components.callback"});@MapperScan("com.netease.is.antispam.text.dao")main():无自定义;启动钩子:TextOpRecordRecover/RedisRecoverServiceV2(@PostConstruct,组件内注册)- Apollo:
app.id=antispam-text_dubbo
facade/http-check-api
- 启动类:
com.netease.is.antispam.text.facade.http.HttpCheckApplication(facade/http-check-api/.../HttpCheckApplication.java) - exclude:
MultipleElasticsearchAutoConfiguration @Enable*:@EnableAspectJAutoProxy、@EnableBusinessClient(→BusinessClientConfiguration,从 ZK/netease-antispam/online/config拉 Product/Target/Secret/Label)、@EnableRecoverComponent、@EnableConfigurationProperties({RateLimiterProperties.class})、@EnableRateLimiter、@EnableI18nComponent、@EnableEventClient@ComponentScan({"com.netease.is.antispam.text","com.netease.is.antispam.http.common","com.netease.is.platform.components.rate.limiter"})- Kafka:由
configuration/KafkaConfiguration的@EnableKafka拉起kafkaListenerContainerFactory(app.kafka.bootstrap.servers、并发 20) main():无自定义;钩子:DealingPoolService @PostConstruct、TextCheckRateLimiter @PostConstruct- Apollo:
app.id=http-check-api,namespaces=application,circuitbreaker,text_common
facade/http-api
- 启动类:
com.netease.is.antispam.text.facade.http.HttpApplication(facade/http-api/.../HttpApplication.java) @SpringBootApplication(无 exclude);@EnableAspectJAutoProxy(proxyTargetClass=true)、@EnableTransactionManagement、@EnableBusinessClient、@EnableRateLimiter、@EnableI18nComponent、@EnableEventClient@ComponentScan({"com.netease.is.antispam.text","com.netease.is.antispam.http.common","com.netease.is.platform.components.rate.limiter"});@MapperScan("com.netease.is.antispam.text.dao")- 无 Kafka;
@EnableCdcDataSubscribe在源码中被注释 main():无自定义;钩子:RedisRecoverServiceV2等组件@PostConstruct- Apollo:
app.id=antispam-text_http-api
storage
- 启动类:
com.netease.is.antispam.text.storage.StorageApplication(storage/.../StorageApplication.java) @SpringBootApplication(无 exclude);@EnableAspectJAutoProxy(proxyTargetClass=true)、@EnableTransactionManagement、@EnableDistributeId、@EnableBusinessClient、@EnableRecoverComponent@ComponentScan({"com.netease.is.antispam.text","com.netease.is.antispam.components.callback"});@MapperScan("com.netease.is.antispam.text.dao")- Kafka:
configuration/KafkaConfiguration(@EnableKafka)+ 平台自研@KafkaConsumer(com.netease.is.antispam.components.kafka.consume)拉起 Cdc/ES/归档/反馈等消费者 main():无自定义;钩子:YidunQosApplication(@QosRegister/QosApplication,doOffline()pause Kafka 容器后Thread.sleep(3000))、StorageApolloConfig @PostConstruct@EnableCdcDataSubscribe被注释(靠 Kafka 而非 CDC 落库)- Apollo:
app.id=antispam-text_storage
scheduler
- 启动类:
com.netease.is.antispam.text.scheduler.SchedulerApplication(scheduler/.../SchedulerApplication.java) @SpringBootApplication;@EnableAspectJAutoProxy(proxyTargetClass=true)、@EnableTransactionManagement、@EnableElasticJob、@EnableConfigurationProperties({TextElasticsearchMigrationWorker.Properties.class})、@EnableBusinessClient、@EnableRateLimiter@ComponentScan({"com.netease.is.antispam.text","...components.callback.clean","...components.callback.activeretry"});@MapperScan("com.netease.is.antispam.text.dao")main():无自定义;钩子:YidunQosApplication、TextElasticsearchMigrationWorker @PostConstruct;任务类用@ElasticJobConf注册- Apollo:
app.id=antispam-text_scheduler
async-consume
- 启动类:
com.netease.is.antispam.text.async.consume.check.AsyncConsumeApplication - exclude:
MultipleElasticsearchAutoConfiguration、YidunCircuitBreakerAutoConfiguration(异步消费不启用业务熔断组件) @EnableAspectJAutoProxy、@EnableBusinessClient、@EnableRecoverComponent、@EnableConfigurationProperties({RateLimiterProperties.class})、@EnableRateLimiter、@EnableEventClient@ComponentScan({"com.netease.is.antispam.text","com.netease.is.antispam.http.common","...rate.limiter"})- 钩子:
configuration/YidunQosApplication;Apollo:app.id=antispam-text_async-consume
facade/http-grammarfix-api
- 启动类:
com.netease.is.antispam.text.facade.http.HttpCheckApplication(与 http-check-api 同名不同目录) - 无 Dockerfile、不在 kubernetes.yml,仅作接口源码存在
console/api
- 启动类:
com.netease.is.antispam.text.console.api.ConsoleApplication:@SpringBootApplication+@ComponentScan({"com.netease.is.antispam.text"}) - 无 Dockerfile、不在 kubernetes.yml
五、启动前置依赖
| 依赖 | 配置键/地址 | 阻塞 or 弱依赖 | 配置文件 |
|---|---|---|---|
| ZooKeeper(Dubbo 注册 + business client) | zookeeper://zk-0.zookeeper.yidun-infra:2181,group /yidun/antispam/online-new/yidun-antispam-dubbo;business.client.options.zookeeper.rootPath=/netease-antispam/online/config | 阻塞 | application*.properties / application-private.properties |
| TiDB / MySQL | jdbc:mysql://tidb-cluster0-tidb.tidb.svc:4000/antispam(com.mysql.cj.jdbc.Driver) | 阻塞(http-api/storage/scheduler/dubbo-provider) | application-private.properties |
| Kafka | kafka-yidun-{0,1,2}.kafka-yidun-headless.yidun-infra:9092(app.kafka.bootstrap.servers) | 阻塞(http-check-api/storage/async-consume) | application-private.properties |
| Redis Sentinel | redis0-redis-ha.yidun-infra.svc:26379,master master01 | 阻塞(限流/缓冲池) | application-private.properties |
| Elasticsearch | http://elasticsearch-yidun-master.yidun-infra.svc:9200(elasticsearch.multiple.*) | storage/scheduler 弱依赖(多实例,miss 可回落 default) | application-private.properties |
| Apollo | app.id 见各单元;apollo.bootstrap.namespaces | 阻塞(apollo.bootstrap.enabled=true),apollo.cache-dir=./apollo/cache 提供本地兜底 | application.properties |
| mplatform-event | @EnableEventClient 依赖事件中心 | 弱依赖 | http-check-api/http-api |
| 上游检测引擎(keyword/rule/list/llm/guardian/textclassify) | 经 Dubbo 调用各 checker 提供方 | 弱依赖(决策为 checker 内部降级) | Dubbo 配置 |
六、启动参数与 Profile
-Dspring.profiles.active:默认dev(application.properties中spring.profiles.active=dev);Dockerfile 与 k8s 均覆盖为private;另有test、online、jiande-online、beijing-online、frankfurt-online、tokyo-online、virginia-online、xjp-online、guizhou-online、guiyang-online、tianjin-online、hz-aliyun-online等区域 profile- Maven profile(
buildAll.sh以-P{private}全量构建;模块build.sh -e {env}):default/dev/test/online/private/xjp-test/xjp-online等 - JAVA_OPTS(Dockerfile):
-Xmx512m -Xms512m -XX:+UseG1GC -Dspring.profiles.active=private(dubbo-provider/http-api/storage/scheduler);http-check-api/async-consume 为-Xmx1024m -Xms1024m - JAVA_OPTS(k8s 覆写):
-Xmx1024m -Xms1024m -XX:+UseG1GC -Dspring.profiles.active=private(所有 Deployment 统一) - base image:
private-registry.nis.netease.com/library/base-image:jdk17-kylin;镜像仓库:private-registry.nis.netease.com/yidun(构建推送)→private-registry.yidun.internal/yidun(k8s 拉取,tag1.0.0.private) - build.sh 关键参数:
-e(BUILD_ENV,默认 private)、-r(仓库)、-t(tag,默认base-250409-kylin)、-c(是否编译,buildAll.sh传false)、-p(是否git pull);docker buildx build --platform linux/amd64,linux/arm64 --push;结束后调用podupdate.sh更新 K8s Pod
七、启动期踩坑
scheduler有独立 Dockerfile 与 Apolloapp.id=antispam-text_scheduler,但不在 kubernetes.yml 中部署——手工部署需自行补清单,否则任务长期不生效。- storage 与 http-api 的
@EnableCdcDataSubscribe均被源码注释,不要为其配置 CDC 数据源;文本结果落库走 Kafka 消费。 dubbo-provider用@SpringBootApplication(exclude=DataSourceAutoConfiguration)却仍保留@MapperScan("...text.dao"),数据源由 Druid/自定义装配提供,缺少对应配置会报 DAO 初始化失败。http-check-api依赖@EnableEventClient(mplatform-event),事件中心不可用时相关 bean 装配失败会阻断启动。storage的YidunQosApplication.doOffline()会 pause 全部 Kafka 容器并sleep(3000),因此 k8sterminationGracePeriodSeconds必须大于该值,否则优雅下线被强杀、位点回退导致重复消费。configuration/KafkaConfiguration上@Configuration在源码中处于注释状态,仅保留@EnableKafka;排查 Kafka 未启用时先确认该配置类是否被组件扫描注册,勿只盯@KafkaConsumer标注入。http-check-api的 base profile 端口是8110、private 是8080,而 k8s Service/探针统一指向8080,本地用 base profile 起服务会与探针口径不一致。facade/http-grammarfix-api有启动类但无 Dockerfile,属"半成品"单元;误以为它会随buildAll.sh产出镜像会扑空。
八、跨模块前置
启动前须已就绪的其它模块服务:
antispam-business:business client 从 ZK/netease-antispam/online/config读取 Product/Target/Secret/Label 等配置(@EnableBusinessClient),未就绪时准入校验与策略集解析全部失败(硬前置)antispam-keyword/antispam-rule/antispam-list/antispam-llm/antispam-guardian/antispam-textclassify:dubbo-check-provider检测链的 Dubbo 提供方,缺失则对应 checker 降级(软前置)antispam-bill:计量与归档统计枚举/消息依赖(编译期 + 运行期 topic 消费)yidun-mplatform-event:事件中心,http-check-api/http-api的@EnableEventClient依赖- 基础设施:ZooKeeper、Kafka、Redis Sentinel、TiDB、Elasticsearch、Apollo