外观
antispam-list — 名单数据中枢(黑白名单/忽略词命中 + 增删改查 + 回调同步 + 定时清理)
分层:名单数据层 | 部署单元:4 个有 Dockerfile(k8s 部署其中 3 个)| 数据库:MySQL/TiDB
antispam| base image:private-registry.nis.netease.com/library/base-image:jdk17(tagbase-250409)
一、模块定位与架构
它维护用户/账号/IP/设备/音频 MD5/视频 MD5/直播账号等维度的黑白名单(白名单放行、黑名单拦截、必审/预审转人工)以及“忽略词/预处理”词库(检测前清洗无意义词、URL、正则、替换词)。对上游检测业务提供毫秒级内存命中查询,对外部客户与运营后台提供名单/忽略词增删改查,并把名单变更通过 Kafka 主动回调推给客户。
架构分层
- 协议层:
facade/http-api/controller(/v1|v2/list/*、/v1|v2|v3/pretreatment/*、/v1/analysis/*、/v1/antispamList/callback/results,含签名校验) +facade/dubbo-api(check 契约) +facade/dubbo-admin-api(管理写契约) - 写服务层:
service/business(ListServiceImpl、PretreatmentServiceImpl、ListManager、ListRecord/PretreatmentRecord流水) - 数据层:
service/base(manager+dao+ MyBatis Mapper) - 命中层:
facade/dubbo-provider的内存结构ListRegistry(名单树)、PretreatmentRegistry+DynamicTrie(忽略词 Trie)、IpRegionNewRegistry(IP 地区)
关键数据流
- 写请求(HTTP / Dubbo admin)→
ListServiceImpl.insert/batchAdd(校验 needConfirm,写 List 表 + ListRecord 流水) ListManager.insert置status=1、callbackStatus=TOBE_CALLBACK,投递 KafkaAntispam_List_CallbackListIncrementSyncService.sync(约 60s)扫描ListRecord变更ListRegistry.put/remove更新内存树- 命中查询
CheckService.listCheck按productId→entityType→entity→targetId逐级查树并回传 StrategyChangeTask发TARGET_VERSION_CHANGE_V2事件通知下游目标版本刷新
二、可部署服务清单
| 部署单元目录 | artifactId | 镜像名 | 端口 | 服务类型 | 独立 Dockerfile | 是否进 kubernetes.yml |
|---|---|---|---|---|---|---|
facade/dubbo-provider | antispam-list-facade-dubbo-provider | private-registry.yidun.internal/yidun/antispam-list-dubbo-provider:1.0.0.private | Dubbo port=-1 | Dubbo Provider(命中查询) | 有 | 是(replicas=2,cpu 1/2,mem 1024/2048) |
facade/dubbo-admin-provider | antispam-list-facade-dubbo-admin-provider | private-registry.yidun.internal/yidun/antispam-list-dubbo-admin-provider:1.0.0.private | Dubbo port=-1 | Dubbo Provider(管理写) | 有 | 是(replicas=2,cpu 1/2,mem 1024/2048) |
scheduler | antispam-list-scheduler | private-registry.yidun.internal/yidun/antispam-list-scheduler:1.0.0.private | 无 Web | ElasticJob + Kafka Consumer | 有 | 是(replicas=1) |
facade/http-api | antispam-list-facade-http | 若构建为 antispam-list-http-api:base-250409 | 8321(base;无 private 覆写文件) | HTTP | 有 | 否 |
console/api | antispam-list-console-api | 无(不构建) | — | 控制台后端 | 无 | 否 |
三、同模块启动顺序
facade/dubbo-provider与facade/dubbo-admin-provider可并行 —— 前者是读侧命中(内存树 + 全量同步),后者是写侧(落库 + 发回调),二者只共享同一 DB 与 Kafka,无进程内依赖。scheduler—— 跑名单释放/删除/清理/变更事件任务并消费 Kafka,依赖 DB 中已有名单数据与 admin 侧写的流水。facade/http-api—— 对外 HTTP 表单接口,读侧靠 Dubbo 与内存数据,最后启动即可。
四、逐服务启动逻辑
facade/dubbo-provider
- 启动类:
com.netease.is.antispam.list.facade.dubbo.CheckDubboApplication(facade/dubbo-provider/src/main/java/.../CheckDubboApplication.java) @SpringBootApplication:无 exclude;@EnableAspectJAutoProxy、@EnableScheduling、@EnableBusinessClient- 同包(
...list.facade.dubbo.configuration)被默认组件扫描捞到的两个配置:ApolloConfiguration:@Configuration @EnableApolloConfig @ConditionalOnProperty(name="apollo.bootstrap.enabled", havingValue="true", matchIfMissing=true)CheckConfiguration:@ComponentScan({...list.service, ...list.manager, ...list.kafka})+@MapperScan("...list.dao")+@EnableScheduling,并implements SchedulingConfigurer→taskRegistrar.setScheduler(new ScheduledThreadPoolExecutor(${app.task.pool-size}))
- 关键配置:
app.id=antispam-list-dubbo-check、dubbo.application.id=antispam-list-dubbo、dubbo.registry.id=listCheckRegistry、list.region=cn、list.init.entityTypes=1,2,3,4,5、business.client.options.operateTargetTypes=TARGET,CLIENT_TARGET_SCENE,CLIENT_SCENE,PRODUCT main():仅 run- 启动钩子(一批
@PostConstruct全量同步,启动期最重):ListFullSyncService、IpRegionFullSyncService、PretreatmentFullSyncService(名单/IP 地区/忽略词全量加载) +CheckerPreloader、DubboWarmUpFilter(Dubbo 预热/Qos 过滤)
facade/dubbo-admin-provider
- 启动类:
com.netease.is.antispam.list.facade.dubbo.AdminDubboApplication @SpringBootApplication:无 exclude;@EnableAspectJAutoProxy、@EnableFacadeAutoConfiguration、@EnableBusinessClient、@EnableDistributeId@EnableFacadeAutoConfiguration→@Import(FacadeConfiguration)(service/business/.../configuration/facade/):@ComponentScan({...list.service, ...list.manager, ...list.kafka})+@MapperScan("...list.dao")+@Bean RedisComponent- 关键配置:
dubbo.application.id=antispam-list-admin-dubbo、dubbo.registry.id=listAdminRegistry、private 使用 Redis Cluster(spring.redis.cluster.nodes=10.196.x.x:61xx,...硬编码 +spring.redis.cluster.max-redirects=3) main():仅 run;启动钩子:未发现
scheduler
- 启动类:
com.netease.is.antispam.list.scheduler.SchedulerApplication @EnableElasticJob、@SpringBootApplication、@EnableScheduling、@EnableSchedulerAutoConfiguration、@EnableBusinessClient、@EnableDistributeId@EnableSchedulerAutoConfiguration→@Import(SchedulerConfiguration)(service/business/.../configuration/scheduler/):@ComponentScan({...list.service, ...list.manager, ...list.kafka})+@MapperScan("...list.dao");scheduler 模块自身的...list.scheduler.configuration.SchedulerConfiguration也在启动类包下被扫到- Kafka Consumer:
groupId=netease-antispam-stat、concurrency=18(netease.kafka.consumer.cluster.jdkafka.*) - ElasticJob 任务:
listStatTask、ipRegionReleaseTask、listCleanTask、listDeleteTask、listReleaseTask、strategyChangeTask、antispamErrorListActiveCallbackWorker - 内置
YidunQosApplication;main()仅 run
facade/http-api
- 启动类:
com.netease.is.antispam.list.facade.http.HttpApplication(同包configuration/HttpConfiguration) @SpringBootApplication:无 exclude;@EnableAspectJAutoProxy、@EnableBusinessClient、@EnableDistributeId- 关键配置:
server.port=8321、business.client.options.operateTargetTypes=SECRET,PRODUCT,...,CLIENT_SCENE、redis.lock.expireTime=100/redis.lock.retryTime=30、distribute.id.namespace=antispam-list main():仅 run;启动钩子:未发现
五、启动前置依赖
| 依赖 | 配置键 / 地址 | 阻塞 or 弱依赖 | 配置文件 |
|---|---|---|---|
| MySQL/TiDB | spring.datasource.url=jdbc:mysql://tidb-cluster0-tidb.tidb.svc:4000/antispam | 阻塞 | application-private.properties |
| ZK — Dubbo 注册中心 | dubbo.registry.address=zookeeper://zk-0.zookeeper.yidun-infra:2181?backup=...,group /yidun/antispam/online-new/yidun-antispam-dubbo | 阻塞 | application-private.properties |
| ZK — business.client | business.client.options.zookeeper.address=zk-0/1/2.zookeeper.yidun-infra:2181,rootPath /netease-antispam/online/config | 阻塞 | application-private.properties |
| Redis | dubbo-provider / scheduler:Sentinel(spring.redis.sentinel.master=master01,nodes=redis0-redis-ha.yidun-infra.svc:26379);dubbo-admin-provider:Cluster(硬编码 IP) | 阻塞 | application-private.properties |
| Kafka | dubbo-provider:new.app.kafka.bootstrap.servers=kafka-yidun-0/1/2...:9092;admin:yidun-test-commonkafka01/02/03;scheduler:netease.kafka.consumer.cluster.jdkafka.bootstrap.servers | scheduler 阻塞 / 其余弱 | application.properties + private |
| ZK — ElasticJob | elastic.job.zk.* | 阻塞(scheduler) | scheduler 配置 |
| ZK — 分布式 ID | distribute.id.namespace=antispam-list,distribute.id.zookeeperHost=zk-0/1/2... | 阻塞 | application.properties + private |
| Apollo | app.id=antispam-list-dubbo-check,apollo.bootstrap.enabled=true,namespaces application | 弱 | application.properties |
六、启动参数与 Profile
-Dspring.profiles.active:base 默认dev;仓库含dev/test/test-pre/private/online/bj-online/gz-online/...多地域配置;容器统一private- maven profile:本仓库根
pom.xml未自定义 profile,-P private由父/平台 POM 提供 - JAVA_OPTS:Dockerfile 统一
-Xmx512m -Xms512m -XX:+UseG1GC -Dspring.profiles.active=private;k8s 覆写-Xmx1024m -Xms1024m -XX:+UseG1GC ...(与 2048Mi limit 匹配) - base image 与镜像仓库:
private-registry.nis.netease.com/library/base-image:jdk17;build.shDOCKER_REPOSITORY=private-registry.nis.netease.com/yidun,tagbase-250409;k8s 拉取private-registry.yidun.internal/yidun/antispam-list-*:1.0.0.private build.sh关键参数:MODEL_NAME=facade/dubbo-provider|facade/dubbo-admin-provider|facade/http-api|scheduler;BUILD_ENV=private;DOCKER_IMAGE_NAME=antispam-list-dubbo-provider|antispam-list-dubbo-admin-provider|antispam-list-http-api|antispam-list-scheduler;DOCKER_IMAGE_TAG=base-250409;docker buildx build --platform linux/amd64,linux/arm64 --push
七、启动期踩坑
- Redis 形态不一致:
dubbo-admin-provider用 Redis Cluster(spring.redis.cluster.nodes直接写死10.196.x.x:61xxIP),而dubbo-providerIP 变更时 admin 侧会先挂。 - scheduler Kafka group 复用风险:
groupId=netease-antispam-stat与其它模块同名,若同 group 被多处消费会造成消息被分摊/重复消费。 - dubbo-provider 启动期全量同步:
ListFullSyncService/IpRegionFullSyncService/PretreatmentFullSyncService在@PostConstruct全量加载,DB 或 ZK 不通即启动失败(无降级)。 app.task.pool-size=4:CheckConfiguration把 Spring@Scheduled线程池固定为 4,与增量同步等任务并存时并行度偏低(NettyProcessor/定时任务易排队)。list.region与list.init.entityTypes决定加载范围:list.region=cn、list.init.entityTypes=1,2,3,4,5—— 配错会导致名单只加载部分维度(漏检)或加载无关地域(内存浪费),且不报错。- http-api 无 private 配置:
facade/http-api只有 base(server.port=8321)等属性,没有application-private.properties,private 环境下端口/数据源只能靠外部注入或沿用 base。 dubbo.protocol.register=false与dubbo.provider.filter=qos-access:读侧不注册到注册中心属预期,排查注册中心缺失 Provider 时不要误判。console/api不构建:有启动类但与其它模块一样不在buildAll.sh的构建集合内。- admin 侧 Kafka 指向测试集群:
dubbo-admin-provider的new.app.kafka.bootstrap.servers=yidun-test-commonkafka01/02/03.service.163.org:9092,而 dubbo-provider 与 scheduler 用kafka-yidun-0/1/2...-headless.yidun-infra:9092,回调消息可能落到不同集群。 - 两个 Dubbo 应用同端口语义:
dubbo.protocol.port=-1让 dubbo-provider 与 dubbo-admin-provider 都用随机端口,若误改成固定端口并同机部署会冲突。 list.sync.delay=5000(5s)增量同步:ListIncrementSyncService按该延迟扫描ListRecord,名单写入到命中最长可见延迟约等于“扫描周期 + delay”,调优命中时效必须动这里。- 名单命中优先级固定:检测侧按
productId→entityType→entity→targetId逐级查树,白名单放行优先于黑名单拦截的语义写死在CheckService内,改优先级需改代码而非配置。
八、跨模块前置
- 配置源依赖
antispam-business:经business.client读取ProductCache/TargetCache/TargetConfigCache/ClientSceneCache/SecretInfoCache及TargetFacade/TargetCheckerFacade/TargetConfigFacade—— 启动期配置读取(阻塞级),建议antispam-business先运行。 - 运行期弱依赖:
netease-antispam的LiveAccountListFacade(直播账号名单联动被动回调)、com.netease.is.loclib.ip(IP 定位库,随包引入)、antispam-components(Kafka/回调组件)。 - 上游调用方:文本/图片/音频/视频/直播各
*blacklist*/*pretreatment*/*region*检测 service 与antispam-cms、antispam-rule的 validate 均import ...list.facade.dubbo.api.*,属运行期消费者而非启动前置。 - 下游消息面:
Antispam_List_Callback(客户主动回调)与EventMessage.TOPIC的TARGET_VERSION_CHANGE_V2(目标版本刷新)为外发消息,需 Kafka 可用。 - 除此之外无硬依赖。