news 2026/10/22 1:00:47

蓝鲸 CMDB 资源变更事件存储表详解:cc_WatchChain 事件链与 cc_WatchToken 监控令牌

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
蓝鲸 CMDB 资源变更事件存储表详解:cc_WatchChain 事件链与 cc_WatchToken 监控令牌
  • 后端
  • 企业应用
  • 运维

【免费下载链接】bk-cmdb

蓝鲸智云配置平台(BlueKing CMDB)

项目地址:https://gitcode.com/gh_mirrors/bk/bk-cmdb
点击查看免费下载

本文深入剖析蓝鲸智云配置平台(bk-cmdb)中资源变更事件(Watch)的底层存储设计,聚焦cc_{资源类型}WatchChain事件链表族与cc_WatchToken监控令牌表的表结构、字段语义,并结合 cacheservice 的源码实现,说明事件是如何被写入、游标如何演进、令牌如何实现断点续传。读完本文,你将能理解 CMDB 事件订阅机制的数据落地方式,并能对照仓库源码定位到对应的读写链路。

一、事件机制与两张核心表的关系

蓝鲸 CMDB 基于 MongoDB 的 change stream(变更流)能力实现资源变更事件订阅:当主机、业务、模型、实例关联等数据发生变化时,系统会把变更事件按资源类型分门别类地持久化到 MongoDB 中,供外部系统(如蓝鲸其他 SaaS、数据同步平台)通过watch接口拉取。

整个事件存储体系由两类表构成:

表族作用
cc_{资源类型}WatchChain存放某类资源的变更事件(事件链节点),所有 WatchChain 表结构统一
cc_WatchToken存放每个数据集合的监控令牌(断点续传游标),用于跟踪指定数据集合的变化情况

从源码看,链表的命名规则在 cacheservice/event/key.go 中定义:ChainCollection()返回k.collection + "WatchChain",即在原数据表名(如cc_HostBase)后拼接WatchChain得到事件链表名(如cc_HostBaseWatchChain)。而cc_WatchToken的表名常量定义于 common/tablenames.go。

二、cc_{资源类型}WatchChain:事件链表族

2.1 覆盖的资源类型与表清单

变更事件相关的表具有统一结构,当前仓库归类包含如下 21 张事件链表:

表名称作用
cc_ApplicationBaseWatchChain存放业务变更事件信息
cc_BizSetBaseWatchChain存放业务集变更事件
cc_bizSetRelationMixedWatchChain存放业务集关系变更事件
cc_ClusterBaseWatchChain容器数据纳管——存放集群变更事件
cc_HostBaseWatchChain存放主机变更事件信息
cc_HostIdentityMixedWatchChain存放主机身份变更事件信息
cc_InstAsstWatchChain存放实例关联变更事件信息
cc_MainlineInstanceWatchChain存放主线实例变更事件信息
cc_ModuleBaseWatchChain存放模块变更事件信息
cc_ModuleHostConfigWatchChain存放模块主机关联关系变更事件信息
cc_NamespaceBaseWatchChain容器数据纳管——存放命名空间变更事件信息
cc_NodeBaseWatchChain容器数据纳管——存放节点变更事件信息
cc_ObjectBaseWatchChain存放模型(实例)变更事件
cc_PlatBaseWatchChain存放管控区域变更事件信息
cc_ProcessInstanceRelationWatchChain存放进程实例关联关系变更事件信息
cc_ProcessWatchChain存放进程变更事件信息
cc_ProjectBaseWatchChain存放项目变更事件信息
cc_SetBaseWatchChain存放集群(Set)变更事件信息
cc_SetTemplateWatchChain存放集群模板变更事件信息
cc_WorkloadBaseWatchChain容器数据纳管——工作负载变更事件信息
cc_PodBaseWatchChain容器数据纳管——Pod 变更事件信息

其中MixedWatchChain类表(如cc_HostIdentityMixedWatchChain、cc_bizSetRelationMixedWatchChain)对应的是混合事件类型:源码中HostIdentifier游标类型被注释为 "a mixed event type, which contains host, host relation, process events etc. and finally converted to host identifier event"(见 cursor.go),BizSetRelation同理由业务集与业务事件转换而来(cursor.go)。它们由 cacheservice 的 mix-event 处理器 消费多条基础事件链后合并生成。

2.2 统一的表结构

所有 WatchChain 表采用统一字段结构:

字段类型描述
_idObjectId数据唯一 ID
idNumberLong自增 ID
cluster_timeISODate事件时间
oidString事件 ID(事件所属文档的_id)
typeString操作类型
tokenString数据库恢复令牌(MongoDB resume token)
cursorStringCMDB 的事件游标
inst_idNumberLong实例 ID
bk_supplier_accountString开发商 ID

这一结构在 Go 侧由 common/watch/types.go 的ChainNode结构体直接对应,其 bson 标签与上表逐字段吻合:

  • id:自增 ID,searchFollowingEventChainNodesByID正是基于它做id > startID的顺序查询(client.go),保证事件按序消费;
  • cluster_time:事件发生时间,查询最早/最新事件节点时都会以cluster_time >= now - TTL作为过滤条件(client.go);
  • oid:事件所属文档的_id值,用于事件详情回查;
  • type:操作类型,仅支持create/update/delete(common/watch/types.go);
  • token:MongoDB 的 resume token(_data十六进制串),空字符串表示链头节点;
  • cursor:由 CMDB 自行生成、与 MongoDB resume token 一一对应的游标,作为对外暴露的事件定位标识;
  • inst_id:实例 ID,为后续事件聚合预留;
  • bk_supplier_account:事件所属开发商账号,实例类事件还会借助它定位分表(client.go)。

三、cc_WatchToken:监控令牌表

3.1 作用

cc_WatchToken用于存储数据库表的监控令牌信息,监控令牌用于跟踪指定数据集合的变化情况,是整个事件订阅实现“断点续传 / 崩溃恢复”的关键。

3.2 表结构

字段类型描述
_idString数据库表名(即被监控集合的名字)
idNumberLong自增 id
tokenString令牌
cursorString事件游标
start_at_timeISODate开始时间

该表在源码中的数据结构是 common/watch/types.go 的LastChainNodeData:

type LastChainNodeData struct { Coll string `json:"_id" bson:"_id"` // 数据库表名 ID uint64 `json:"id" bson:"id"` // 自增 id Token string `json:"token" bson:"token"` // 令牌 Cursor string `json:"cursor" bson:"cursor"` // 事件游标 StartAtTime types.TimeStamp `json:"start_at_time,omitempty" bson:"start_at_time,omitempty"` // 开始时间 }

注意该表以被监控集合名(如cc_HostBase)作为文档_id,一行对应一个被监控集合,而不是像 WatchChain 表那样每行一条事件。

四、事件链的写入与令牌更新流程

4.1 写入链路

从源码结构看,事件写入由 cacheservice/event/flow 中的事件流(flow)负责:cacheservice 基于 MongoDB change stream 监听各资源数据表,将insert/replace/update/delete等操作转换为事件后写入对应的WatchChain表,同时把事件详情写入 Redis 缓存(getEventDetailFromRedis的读取逻辑见 client.go,事件链节点仅保存游标与定位信息,详情按DetailKey(cursor)存放在 Redis)。

4.2 令牌的读取与回写

令牌处理器flowTokenHandler是令牌表的主要读写方(flow/handler.go):

  • 回写:事件成功入库后,setLastWatchToken以{"_id": key.Collection()}为过滤条件Updatecc_WatchToken,把最新 token 写入对应集合的行;
  • 读取:GetStartWatchToken优先从cc_WatchToken取 token;若该表无记录(如首次启动),则回退到对应WatchChain表按id倒序取最后一条节点的 token;
  • 重置:当 watch 遇到致命错误时,resetWatchToken将 token 清空并把start_at_time设置为给定时间,下次 watch 从该时间点重新开始,避免误用错误的令牌导致事件丢失。

4.3 游标回退兜底

在读取端(event/watch/client.go),当用户传入的 cursor 在WatchChain表中找不到对应节点时,会回退到cc_WatchToken按_id = 集合名查找id,再从该id继续向后拉取事件。这说明cc_WatchToken中的id承担了“链尾锚点”的作用,为链节点因 TTL 过期被清理后的场景提供兜底。

五、游标(Cursor)与令牌(Token)的设计细节

5.1 CMDB 自定义游标的编码

WatchChain 表中的cursor并非 MongoDB 原始 resume token,而是 CMDB 自定义编码后的字符串。编码逻辑见 common/watch/cursor.go:游标由 7 个字段用\r分隔后整体做 base64 编码:

版本号 \r 资源类型ID \r oid \r 操作类型 \r 集群时间秒 \r 集群时间纳秒 \r 唯一键(可选)

其中资源类型 ID 与CursorType一一映射(cursor.go),例如host→2、biz→4、object_instance→8、kube_workload→20 等。UniqKey用于区分同一资源类型下不同实例的事件,如通用实例事件以实例 ID 作为唯一键、工作负载事件以类型:ID组合作为唯一键(cursor.go),保证游标在重试场景下可重入且唯一。

解码时(cursor.go),程序会校验 base64 格式、版本号、资源类型、oid 格式(普通事件须为十六进制 ObjectId,inst_asst事件须为整数串)以及集群时间字段,任一步失败即拒绝该游标。

5.2 特殊游标:NoEventCursor

当某段时间内没有任何事件时,系统会返回NoEventCursor(MQ0xDTVlYTZkM2YzOTRjMWY1ZDk4NmU5YmQ4Ng1ub0V2ZW50DTENMQ==,见 cursor.go)。消费者收到该游标后,下一次 watch 应从链头开始拉取;服务端在searchFollowingEventChainNodes中对此做了专门分支处理(client.go)。

5.3 事件类型与操作类型的映射

MongoDB change stream 的操作类型与 CMDB 事件类型之间通过ConvertOperateType转换(common/watch/types.go):

MongoDB 操作类型CMDB 事件类型
insertcreate
replace/updateupdate
deletedelete
其他(含drop/rename/invalidate等)unknown

六、TTL 与数据生命周期

WatchChain 事件并非永久保留。从 cacheservice/event/key.go 可以看到,各资源 Key 的ttlSeconds统一为6 * 60 * 60,即6 小时。该 TTL 在多个环节生效:

  • 查询最早/最新事件时,只扫描cluster_time在最近 6 小时内的节点(client.go);
  • WatchWithStartFrom校验bk_start_from与当前时间差必须在 TTL 范围内,否则返回参数错误(watch.go);
  • 查询 cursor 对应节点时同样附加 TTL 过滤(client.go)。

因此消费方应保证拉取频率高于 6 小时,且用游标/令牌持续续传;若事件链节点已过期,系统会借助cc_WatchToken中的锚点 id 兜底续传。

七、watch 接口的三种拉取模式

cc_{资源类型}WatchChain与cc_WatchToken最终服务于对外 watch 接口。在 cacheservice/service/cache.go 中,根据请求参数分流到三种模式:

请求参数处理函数语义
携带bk_cursorWatchWithCursor从指定游标之后继续拉取事件(断点续传)
携带bk_start_from(Unix 秒)WatchWithStartFrom从指定时间点开始拉取
两者皆无WatchFromNow从现在开始监听,返回当前最新事件

当无事件命中时返回Watched=false与NoEventCursor(或原游标),消费方可以此游标进行下一轮轮询。请求参数定义与校验见 common/watch/watch.go:bk_event_types、bk_fields、bk_resource、bk_sub_resource、bk_filter等参数共同决定了从事件链中过滤出哪些节点及其详情字段。

八、小结

  • cc_{资源类型}WatchChain:统一结构的事件链节点表,21 类资源各一张(含混合事件表),字段id(自增有序)、cluster_time(事件时间)、oid(文档 ID)、type(create/update/delete)、token(MongoDB resume token)、cursor(CMDB 编码游标)、inst_id、bk_supplier_account;
  • cc_WatchToken:以集合名为_id的令牌表,保存每个集合最新 token、cursor、id 与起始时间,是断点续传与崩溃恢复的锚点;
  • 两者配合实现“写入事件链 → 回写令牌 → 按游标续传 → 过期兜底”的完整事件订阅闭环,事件详情存于 Redis,链节点默认仅保留 6 小时。

如需进一步深入,可继续阅读 docs/db/instance.md、docs/db/watch.md 及事件流的实现 cacheservice/event/flow/event.go。

  • 后端
  • 企业应用
  • 运维

【免费下载链接】bk-cmdb

蓝鲸智云配置平台(BlueKing CMDB)

项目地址:https://gitcode.com/gh_mirrors/bk/bk-cmdb
点击查看免费下载

相关推荐

上一篇:Superdesign:用AI重新定义你的设计工作流程
下一篇:InputTip开发原理揭秘:AutoHotkey实现输入法状态管理的核心技术

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 1:00:40

内网横向移动到底有多少种方式?一文全部梳理

🔒免责声明:本文内容仅用于授权渗透测试、红蓝对抗、安全运维学习,全部实验操作请在自建隔离靶场完成。未经授权对内网主机进行探测、横向渗透属于违法行为,请严格遵守《网络安全法》相关规定。 前言 当红队拿下内网第一台机器之…

作者头像 李华
网站建设 2026/10/21 22:19:11

大麦自动抢票:3步跑通这个脚本

大麦自动抢票:3步跑通这个脚本 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 开售前 10 秒你还在刷页面,"立即购买"灰得比谁都快。手速再快…

作者头像 李华