外观
antispam-business-image — 点播图片内容安全
分层:业务层 | 部署单元:9 个(k8s 实部署 5 个) | 数据库:MySQL / TiDB(库
antispam) | base image:private-registry.nis.netease.com/library/base-image:jdk17(tagbase-250409-pg)
一、模块定位与架构
模块是易盾「点播图片」内容安全产品的服务端:客户提交图片 URL/base64 后,它编排色情、广告、暴恐、违禁、涉政、恶心、价值观等机器判决,并把机器/人工结论可靠回传客户、留档备查并按检测量计费。它是图片反垃圾从提交、检测链、回调、归档到计费的权威判定与数据留存方。
架构分层
- 协议层:
facade/http-check-api(同步/vX/image/check、base64 检测、离线/vX/image/asyncCheck)、facade/http-api(/vX/image/submit人审抄送、回调拉取、taskId 查询、名单)、facade/dubbo-provider+facade/dubbo-check-provider(Dubbo 服务) - 逻辑层:
check-common(CheckHandler检测编排、ImageDubboCheckService、CheckParamFilter准入切面)、facade/dubbo-check-provider/checker检测链、service/business - 数据层:
service/base(DAO/Manager)、service/business(ES/Redis/Kafka 封装)、storage(Kafka 消费落库/CDC/归档)、async-consume/high-consume(离线检测消费)、scheduler(Elastic-Job) - 公共:
common(包名com.netease.is.antispam.common)、domain、facade/{dubbo-api,dubbo-check-api}、console/api
关键数据流(同步检测 v5)
- 客户 →
ImageCheckV5Controller.check→CheckParamFilter准入(签名、区域、参数长度、批次上限、base64 限制、试用额度) CheckHandler.handleCheckV5→ImageDubboCheckService.imageAsyncCheckDubbo 调用antispam-image引擎afterCheckV5组装 V5 结果 →storageAndBill发入库消息 +REQUEST_STAT计费消息storage消费入库落classify_image并同步 ES;CDC 驱动回调去重、审核计量、归档计费- 客户经
ActiveCallbackHandler(callbackUrl)被动收结论,或主动调/vX/image/query/task拉取
二、可部署服务清单
| 部署单元目录 | artifactId | 镜像名 | 端口 | 服务类型 | 独立 Dockerfile | 是否进 kubernetes.yml |
|---|---|---|---|---|---|---|
facade/dubbo-provider | antispam-business-image-facade-dubbo-provider | antispam-business-image-dubbo-provider | 无 HTTP(Dubbo) | Dubbo Provider(查询/管理) | 有 | 是 |
facade/dubbo-check-provider | antispam-business-image-facade-dubbo-check-provider | antispam-business-image-dubbo-check-provider | 无 HTTP(Dubbo) | Dubbo Provider(checker 链) | 有 | 是 |
facade/http-check-api | antispam-business-image-facade-http-check-api | antispam-business-image-http-check-api | 8080 | HTTP 检测入口 | 有 | 是 |
facade/http-api | antispam-business-image-facade-http-api | antispam-business-image-http-api | 8080 | HTTP 抄送/回调/查询/名单 | 有 | 是 |
storage | antispam-business-image-storage | antispam-business-image-storage | 无 HTTP(Kafka 消费) | Kafka 消费落库 | 有 | 是 |
scheduler | antispam-business-image-scheduler | antispam-business-image-scheduler | 无 HTTP(定时任务) | Elastic-Job | 有 | 否 |
async-consume | antispam-business-image-async-consume | antispam-business-image-async-consume | 无 HTTP(Kafka 消费) | Kafka 异步消费 | 有 | 否 |
high-consume | antispam-business-image-high-consume | antispam-business-image-high-consume | 无 HTTP(Kafka 消费) | Kafka 高消费 | 有 | 否 |
console/api | antispam-business-image-console-api | — | — | 控制台后端骨架 | 无(不构建) | 否 |
common、domain、service/base、service/business、facade/dubbo-api、facade/dubbo-check-api、check-common、file为非部署库模块。
三、同模块启动顺序
facade/dubbo-check-provider— checker 检测链注册 ZK,是检测能力前置facade/dubbo-provider— 数据查询/管理 Dubbo 服务,供 http-api 与控制台调用facade/http-check-api— 检测请求 Dubbo 转发依赖 dubbo-check 已注册facade/http-api— 抄送/回调/查询入口,依赖同库 DAO 与 Dubbo 提供方storage— 消费检测/回调/归档 Kafka;需 Kafka/Redis/DB/ES 就绪scheduler/async-consume/high-consume— 后台任务与离线消费,晚于前五者;scheduler经@EnableElasticJob依赖 ZK
理由:Dubbo 提供方必须先于消费方;storage 消费 http-check-api 写入的 topic,Kafka topic 与 Redis 名单 key 需先可用。
四、逐服务启动逻辑
facade/http-check-api
- 启动类:
com.netease.is.antispam.business.image.facade.http.check.HttpCheckApplication(facade/http-check-api/.../HttpCheckApplication.java) @SpringBootApplication(无 exclude);@EnableAspectJAutoProxy、@EnableBusinessClient(→BusinessClientConfiguration,ZK/netease-antispam/online/config)、@EnableRateLimiter、@EnableOssClient、@EnableRecoverComponent、@EnableScheduling、@EnableI18nComponent、@EnableEventClient、@EnableIsolateComponent@EnableHttpCheckApiAutoConfiguration间接加载HttpCheckApiConfiguration:@ComponentScan({"com.netease.is.antispam.business.image.kafka","...image.redis","...image.version","...image.common","...image.helper","...image.monitor","...components.qps.configuration","...image.ratelimiter","...image.check.common","...image.component","...image.privatisation.half","...image.apollo"})- 无独立
@EnableKafka:Kafka 消费走 isolate 组件(EnableIsolateComponent) main():先JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask()再run- 钩子:
DynamicQpsSyncService @Scheduled(cron="5/10 * * * * ?")、ImageDynamicQpsRateLimiter @PostConstruct、TaskIdHelper @PostConstruct - Apollo:
app.id=antispam-image_http-check,namespaces=application,kafka-isolate,antispam-image_common,antispam-privatization-common,apollo.cache-dir=./apollo/cache
facade/http-api
- 启动类:
com.netease.is.antispam.business.image.facade.http.HttpApplication(facade/http-api/.../HttpApplication.java) @SpringBootApplication;@EnableAspectJAutoProxy、@EnableBusinessClient、@EnableHttpApiAutoConfiguration、@EnableRateLimiter、@EnableOssClient、@EnableRecoverComponent、@EnableI18nComponent、@EnableConfigurationProperties(ImageConfigPrivatisationProperties.class)@EnableHttpApiAutoConfiguration间接加载HttpApiConfiguration:@ComponentScan(service/manager 等)+@MapperScan("com.netease.is.antispam.business.image.dao")main():先设 fastjson 全局开关再run- 钩子:
AppStartupRunner implements ApplicationRunner - Apollo:
app.id=antispam-image_http
facade/dubbo-provider
- 启动类:
com.netease.is.antispam.business.image.facade.dubbo.DubboApplication - exclude:
DataSourceAutoConfiguration @EnableAspectJAutoProxy、@EnableTransactionManagement、@EnableDubboAutoConfiguration(→DubboConfiguration:@ComponentScan+@MapperScan)、@EnableOssClient、@EnableBusinessClient、@EnableRecoverComponent、@EnableRateLimiter;另有ComponentConfiguration @ComponentScan(basePackageClasses={ImageListService,TaskIdHelper,MetricReporter})、@EnableAsyncmain():先设 fastjson 全局开关再run- Apollo:
app.id=antispam-image_dubbo
facade/dubbo-check-provider
- 启动类:
com.netease.is.antispam.business.image.facade.dubbo.CheckDubboApplication @SpringBootApplication;@EnableAspectJAutoProxy、@EnableCheckDubboAutoConfiguration(→CheckDubboConfiguration:@ComponentScan(component/kafka 等))、@EnableConfigurationProperties({BusinessImageProperties.class})、@EnableBusinessClient、@EnableRecoverComponent、@EnableRateLimiter、@EnableOssClientmain():先设 fastjson 全局开关再run;启动后由PreStartHandler.preStart()预启动- 钩子:
ContextRefreshedEventListener(ApplicationListener)、PreStartHandler - Apollo:
app.id=antispam-image_dubbo-check
storage
- 启动类:
com.netease.is.antispam.business.image.storage.StorageApplication @SpringBootApplication;@EnableBusinessClient、@EnableStorageAutoConfiguration(→StorageConfiguration:@ComponentScan+@MapperScan("...image.dao"),并注册tableNameReplaceFilter(includeclassify_image,classify_image_record))、@EnableDistributeId、@EnableRecoverComponent、@EnableRateLimiter、@EnableOssClientmain():先设 fastjson 全局开关 →run→SpringContextHolder.getBean("preStartHandler").preStart()(run 之后的手动钩子)- 钩子:
configuration/YidunQosApplication(doOffline()pause Kafka 容器并 sleep) - Apollo:
app.id=antispam-image_storage
scheduler
- 启动类:
com.netease.is.antispam.image.scheduler.SchedulerApplication(包com.netease.is.antispam.image.scheduler) @SpringBootApplication;@EnableScheduling、@EnableAspectJAutoProxy、@EnableTransactionManagement、@EnableSchedulerAutoConfiguration(→SchedulerConfiguration:@ComponentScan+@MapperScan)、@EnableOssClient、@EnableElasticJob、@QosRegister、@EnableRecoverComponent、@EnableRateLimiter、@EnableBusinessClientmain():先设 fastjson 全局开关再run;钩子:未发现专属钩子(worker 内@PostConstruct);Apollo:app.id=antispam-image-scheduler
async-consume
- 启动类:
com.netease.is.antispam.business.image.async.consume.check.AsyncConsumeApplication @SpringBootApplication;@EnableAspectJAutoProxy、@EnableAsyncConsumeAutoConfiguration(→AsyncConsumeConfiguration:@ComponentScan(kafka/redis 等))、@EnableBusinessClient、@EnableRateLimiter、@EnableOssClient、@EnableRecoverComponent、@EnableScheduling、@EnableEventClient、@EnableIsolateComponentmain():先设 fastjson 全局开关再run;Apollo:app.id=antispam-image_async-consume
high-consume
- 启动类:
com.netease.is.antispam.image.high.consume.HighConsumeApplication(包com.netease.is.antispam.image.high.consume) @SpringBootApplication;@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})、@EnableScheduling、@EnableAspectJAutoProxy、@EnableHighConsumeAutoConfiguration(→HgihConsumeConfiguration:@ComponentScan(callback 等))、@EnableElasticJob、@QosRegister、@EnableRecoverComponent、@EnableRateLimiter、@EnableBusinessClient、@EnableEventClient、@EnableOssClientmain():先设 fastjson 全局开关再run;钩子:BackUpAsyncCheckTask/NormalAsyncCheckTask @PostConstruct- Apollo:
app.id=antispam-image_high-consume
console/api
- 启动类:
com.netease.is.antispam.console.api.ConsoleApplication;存在EnableConsoleAutoConfiguration/SwaggerConfiguration - 无 Dockerfile、不在 kubernetes.yml
共通:所有启动类的
main()都没有业务自定义逻辑,仅统一设置 fastjson 全局开关后再SpringApplication.run。
五、启动前置依赖
| 依赖 | 配置键/地址 | 阻塞 or 弱依赖 | 配置文件 |
|---|---|---|---|
| ZooKeeper | zookeeper://zk-0.zookeeper.yidun-infra:2181,group /yidun/antispam/online-new/yidun-antispam-dubbo;business root /netease-antispam/online/config | 阻塞 | application-private.properties |
| TiDB / MySQL | jdbc:mysql://tidb-cluster0-tidb.tidb.svc:4000/antispam(com.mysql.cj.jdbc.Driver) | 阻塞(http-api/storage/dubbo-provider/scheduler) | application-private.properties |
| Kafka | kafka-yidun-{0,1,2}.kafka-yidun-headless.yidun-infra:9092 | 阻塞(http-check-api/storage/async/high-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.cluster.servers/archive.*) | 弱依赖 | application-private.properties |
| OSS(s3 / netease / aliyun / cos) | oss.client.provider、oss.client.instance-map.* | 弱依赖(抄送/归档上传) | application.properties |
图片 AI 引擎 netease-image | app.image.check-url | 弱依赖(检测链降级) | application.properties |
| Apollo | app.id=antispam-image_*,apollo.cache-dir=./apollo/cache | 阻塞 | application.properties |
六、启动参数与 Profile
-Dspring.profiles.active:application.properties默认dev;Dockerfile 与 k8s 覆盖为private;仍存在test/online等 profile 文件- Maven profile:
buildAll.sh以mvn clean install -P private后遍历各build.sh;模块build.sh -e <env> - JAVA_OPTS(Dockerfile):
-Xmx512m -Xms512m -XX:+UseG1GC -Dspring.profiles.active=private(dubbo-provider 等);http-check-api 为-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;镜像名不一致三处:DockerfileENV APP_NAME=antispam-business-image-facade-<unit>、build.sh 推private-registry.nis.netease.com/yidun/antispam-business-image-<unit>、k8s 拉private-registry.yidun.internal/yidun/antispam-business-image-<unit>:1.0.0.private - build.sh 关键参数:
-e/-r/-t/-c/-p;docker buildx build --platform linux/amd64,linux/arm64 --push - k8s 通用:namespace
yidun,replicas=2,env 注入ENABLE_SKYWALKING=true、SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-skywalking-oap.yidun-infra.svc:11800;livenessProbe/readinessProbe为/health/status
七、启动期踩坑
scheduler、async-consume、high-consume三者都有独立 Dockerfile,但都不在 kubernetes.yml 中部署;离线检测/高消费逻辑需人工补清单才会跑。storage在main()中run之后才手动调preStartHandler.preStart(),该步骤抛错属于"启动流程尾部"异常,容易被只盯 Spring 启动日志的人忽略。- 所有启动类
main()都先设fastjson的DisableCircularReferenceDetect;新增单元若漏掉这一行,序列化行为会与其它单元不一致(循环引用字段被过滤)。 http-check-api开启@EnableScheduling,DynamicQpsSyncService每 10s 拉一次动态 QPS(new.dynamic.qps.*,依赖 Apollo);Apollo 抖动会持续刷错误日志。- OSS 配置 provider 混用:
oss.client.provider=s3但多个 instance 的 provider 又是netease/aliyun/cos,选错实例会导致图片下载/归档上传失败。 - properties 内明文存放 Redis 口令与密钥(如
gpt.auth.token=sk-...);文档与脚本引用时不要外泄。 - 易误判数据库类型:image 的实际数据源是 MySQL/TiDB(
com.mysql.cj.jdbc.Driver),但console/api、dubbo-provider、http-api的 pom 仍引postgresql驱动,配置时以 properties 为准。 http-check-api/async-consume无@EnableKafka(走@EnableIsolateComponent),排查消费不启动时别只找@KafkaListener。
八、跨模块前置
antispam-business:TargetCache/ProductCache/SecretInfoCache/TargetConfigCache/ClientSceneCache全部来自antispam-business-client-spring-starter(@EnableBusinessClient),未就绪则准入与策略解析失败(硬前置)antispam-image:检测链ImageAiCheckService经 Dubbo 调ImageCheckFacade(含 base64/gpt 分组),是机审主引擎antispam-keyword/antispam-rule/antispam-list/antispam-guardian/antispam-textclassify:检测链 checker 的 Dubbo 提供方(软前置)antispam-bill:REQUEST_STAT计量、审核计量、归档计费消息(运行期)netease-antispam(netease-antispam-common):OperatorType/CallbackStatus/CdcOpTypeEnum/HitType等公共常量yidun-mplatform-event:@EnableEventClient依赖事件中心- 基础设施:ZooKeeper、Kafka、Redis Sentinel、TiDB、Elasticsearch、Apollo、OSS