ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

bk-cmdb 查询主机业务关系信息接口 find_host_biz_relations 实战指南:从 ESB 调用到 cc_ModuleHostConfig 底层实现

bk-cmdb 查询主机业务关系信息接口 find_host_biz_relations 实战指南:从 ESB 调用到 cc_ModuleHostConfig 底层实现
  • 后端
  • 企业应用
  • 运维

【免费下载链接】bk-cmdb

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

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

本文围绕蓝鲸智云配置平台(bk-cmdb)开放接口find_host_biz_relations(查询主机业务关系信息)展开,讲解如何通过主机 ID 批量查询主机所属的业务、集群(set)、模块(module)关系。读完本文,你将掌握该接口的 ESB 网关路径与后端真实路由、请求/响应参数规范、500 条上限的校验机制,以及从 host_server 到 coreservice 的完整调用链与数据库落点,能够在蓝鲸 ESB 或代码中正确、高效地调用并解读该接口。

一、接口概览与定位

find_host_biz_relations是 bk-cmdb 对外开放的一组查询类(api_type: query)接口,用于根据主机 ID 批量查询主机与业务之间的关系。它在蓝鲸 ESB(企业服务总线)中的访问路径为:

POST /v2/cc/find_host_biz_relations/

根据 docs/apidoc/cc/cc.yaml 的接口注册定义,该 ESB 路径只是一个代理入口,实际请求会被转发到 bk-cmdb 内部的host_server服务:

配置项值
path/v2/cc/find_host_biz_relations/
suggest_methodPOST
api_typequery
comp_codenamegeneric.v2.cc.cc_component
dest_path/api/v3/hosts/modules/read
dest_http_methodPOST

同时,在蓝鲸 API 网关(apigateway)的资源定义 docs/apidoc/apigw/open/bk_apigw_resources_bk-cmdb.yaml 中,后端资源POST /api/v3/hosts/modules/read的operationId同样为find_host_biz_relations,并默认挂载了限流插件bk-rate-limit(每 1 秒 100 个 token)。

与它功能相近但视角不同的接口是find_host_topo_relation(获取主机与拓扑的关系),其内部路由为POST /api/v3/host/topo/relation/read,同样由 host_server 处理(见 src/scene_server/host_server/service/service_initfunc.go 的GetAppHostTopoRelation),两者的差别在于:find_host_biz_relations直接返回主机与业务、集群、模块的平铺关系记录,而拓扑关系接口还会带上拓扑实例的层级信息。本接口返回的每一条记录都代表了主机与一个模块的一条挂载关系,一台主机可能出现在多个业务、多个集群、多个模块中,因此返回data是一个数组。

二、请求参数详解

2.1 通用请求参数

调用蓝鲸 ESB 接口时,需要在请求体中携带身份认证与公共参数(对应文档中的{{ common_args_desc }}):

字段类型必填说明
bk_app_codestring是应用 ID,如esb_test
bk_app_secretstring是应用密钥
bk_usernamestring是操作者用户名
bk_tokenstring是登录态 Token

2.2 接口参数

字段类型必填说明
bk_host_idarray是主机 ID 数组,ID 数量不能超过 500
bk_biz_idint否业务 ID,指定后仅返回该业务下的主机关系

2.3 请求参数示例

{ "bk_app_code": "esb_test", "bk_app_secret": "xxx", "bk_username": "xxx", "bk_token": "xxx", "bk_biz_id": 1, "bk_host_id": [ 3, 4 ] }

三、接口参数在源码中的校验逻辑

find_host_biz_relations对应的后端处理器是 host_server 服务中的GetHostModuleRelation。首先在路由注册处,/hosts/modules/read被绑定到该方法(src/scene_server/host_server/service/service_initfunc.go):

// get host module relation in app utility.AddHandler(rest.Action{Verb: http.MethodPost, Path: "/hosts/modules/read", Handler: s.GetHostModuleRelation})

处理器实现位于 src/scene_server/host_server/service/module.go:先将请求体解码为HostModuleRelationParameter,然后执行参数校验,再封装成内部请求HostModuleRelationRequest交给逻辑层。

请求体对应的参数结构体定义在 src/common/metadata/hostserver.go:

// HostModuleRelationParameter get host and module relation parameter type HostModuleRelationParameter struct { AppID int64 `json:"bk_biz_id"` HostID []int64 `json:"bk_host_id"` }

其Validate()方法给出了文档所述“数量不能超过 500”的准确实现:

func (h *HostModuleRelationParameter) Validate() (rawError errors.RawErrorInfo) { if len(h.HostID) == 0 || len(h.HostID) > common.BKMaxInstanceLimit { return errors.RawErrorInfo{ ErrCode: common.CCErrArrayLengthWrong, Args: []interface{}{"bk_host_id", common.BKMaxInstanceLimit}, } } return errors.RawErrorInfo{} }

这里有两个关键点:

  • BKMaxInstanceLimit = 500定义在 src/common/definitions.go,是全局的单次批量上限常量。bk_host_id既不能为空数组,也不能超过 500 个,否则返回错误码CCErrArrayLengthWrong = 1199089(见 src/common/errInfo.go)。
  • 参数校验发生在 host_server 入口层,因此即使只查一台主机也必须以数组形式传入(如"bk_host_id": [3]),这与其他许多单值字段的接口习惯不同。

四、响应参数说明

4.1 响应结构

字段类型说明
resultbool请求是否成功,true 成功 / false 失败
codeint返回码,0 表示成功,>0 表示失败错误码
messagestring失败时的错误信息
permissionobject权限信息
request_idstring请求链路 ID
dataobject请求返回的数据

4.2 data 字段说明

字段类型说明
bk_biz_idint业务 ID
bk_host_idint主机 ID
bk_module_idint模块 ID
bk_set_idint集群 ID
bk_supplier_accountstring开发商账号

4.3 响应示例

{ "result":true, "code":0, "message":"success", "permission": null, "request_id": "e43da4ef221746868dc4c837d36f3807", "data": [ { "bk_biz_id": 3, "bk_host_id": 3, "bk_module_id": 59, "bk_set_id": 11, "bk_supplier_account": "0" }, { "bk_biz_id": 3, "bk_host_id": 3, "bk_module_id": 60, "bk_set_id": 11, "bk_supplier_account": "0" }, { "bk_biz_id": 3, "bk_host_id": 3, "bk_module_id": 61, "bk_set_id": 12, "bk_supplier_account": "0" }, { "bk_biz_id": 3, "bk_host_id": 4, "bk_module_id": 60, "bk_set_id": 11, "bk_supplier_account": "0" } ] }

从示例可以看出:主机3同时挂载在模块59/60/61(分属集群11/12),主机4挂载在模块60(集群11),两者同属业务3。因此返回记录条数 = 各主机在各业务下挂载模块的总条数,而不是主机个数。

4.4 data 字段与源码数据结构的对应

响应中的data数组对应源码中的ModuleHost结构体(src/common/metadata/hostcontroller.go):

// ModuleHost TODO type ModuleHost struct { AppID int64 `json:"bk_biz_id,omitempty" bson:"bk_biz_id"` HostID int64 `json:"bk_host_id,omitempty" bson:"bk_host_id"` ModuleID int64 `json:"bk_module_id,omitempty" bson:"bk_module_id"` SetID int64 `json:"bk_set_id,omitempty" bson:"bk_set_id"` OwnerID string `json:"bk_supplier_account,omitempty" bson:"bk_supplier_account"` }

注意omitempty标签的含义:当某条记录的字段为空值时(例如主机尚未归属任何模块),对应的 JSON 字段可能被省略,解析响应时需按“可能缺失”处理。

从 host_server 返回给上层时,实际包装的响应结构是HostModuleResp(src/common/metadata/hostcontroller.go),Data类型为[]ModuleHost,与文档中的data数组一一对应。

五、底层实现:从 host_server 到 coreservice 的完整调用链

在GetHostModuleRelation处理器内部,service 层将外部参数组装为内部请求结构HostModuleRelationRequest(src/common/metadata/core_service.go):

// HostModuleRelationRequest gethost module relation request parameter type HostModuleRelationRequest struct { ApplicationID int64 `json:"bk_biz_id" bson:"bk_biz_id" field:"bk_biz_id" mapstructure:"bk_biz_id"` SetIDArr []int64 `json:"bk_set_ids" bson:"bk_set_ids" field:"bk_set_ids" mapstructure:"bk_set_ids"` HostIDArr []int64 `json:"bk_host_ids" bson:"bk_host_ids" field:"bk_host_ids" mapstructure:"bk_host_ids"` ModuleIDArr []int64 `json:"bk_module_ids" bson:"bk_module_ids" field:"bk_module_ids" mapstructure:"bk_module_ids"` Page BasePage `json:"page" bson:"page" field:"page" mapstructure:"page"` Fields []string `json:"field" bson:"field" field:"field" mapstructure:"field"` }

该结构比外部参数更丰富:支持按bk_biz_id、bk_set_ids、bk_host_ids、bk_module_ids组合过滤,并带分页(page)与字段裁剪(field)。处理器只把外部传入的bk_host_id和可选的bk_biz_id填入其中,并将分页 limit 设为common.BKNoLimit(不限制,一次性返回全部),见 src/scene_server/host_server/service/module.go。

接下来进入 host_server 逻辑层GetHostModuleRelation(src/scene_server/host_server/logics/host.go),这里还有一层兜底校验:

  • 条件为空(Empty()为 true)时返回CCErrCommHTTPBodyEmpty = 1199005;
  • 分页非法时返回CCErrCommPageLimitIsExceeded = 1199059;
  • bk_set_ids超过 200 返回CCErrCommXXExceedLimit = 1199035;
  • bk_module_ids超过 500、bk_host_ids超过 500 同样返回CCErrCommXXExceedLimit。

随后逻辑层通过CoreService().Host().GetHostModuleRelation将请求转发给 coreservice(核心数据服务),最终由 coreservice 的TransferManager执行真正的数据库查询(src/source_controller/coreservice/core/host/transfer/manager.go)。核心逻辑是:把bk_biz_id / bk_host_ids / bk_module_ids / bk_set_ids分别拼装为等值(Eq)或In条件,再查询cc_ModuleHostConfig表(表名常量BKTableNameModuleHostConfig定义在 src/common/tablenames.go),先Count出总条数,再按分页取数并反序列化到[]metadata.ModuleHost,最终返回HostConfigData{Count, Info, Page}结构:

cond = util.SetQueryOwner(moduleHostCond.ToMapStr(), kit.SupplierAccount) cnt, err := mongodb.Client().Table(common.BKTableNameModuleHostConfig).Find(cond).Fields(input.Fields...).Count(kit.Ctx) ... db := mongodb.Client().Table(common.BKTableNameModuleHostConfig). Find(cond). Fields(input.Fields...). Start(uint64(input.Page.Start)). Sort(input.Page.Sort) ... err = db.All(kit.Ctx, &hostModuleArr)

cc_ModuleHostConfig是主机与模块挂载关系的核心表,一条记录代表“一台主机挂载在一个业务模块下”,表内同时冗余存储了bk_biz_id / bk_set_id,这正是该接口能一次性返回业务、集群、模块三层信息的存储基础。

整体调用链可归纳为:

ESB /v2/cc/find_host_biz_relations/ └─► apiserver /api/v3/hosts/modules/read └─► host_server: GetHostModuleRelation (service/module.go) └─► host_server 逻辑层 (logics/host.go) └─► coreservice: TransferManager.GetHostModuleRelation └─► MongoDB 表 cc_ModuleHostConfig

六、在代码与前端中的实际使用方式

6.1 通过 apimachinery SDK 调用

bk-cmdb 的 Go 客户端封装了该接口,见 src/apimachinery/apiserver/api.go:

// GetHostModuleRelation TODO func (a *apiServer) GetHostModuleRelation(ctx context.Context, h http.Header, params mapstr.MapStr) (resp *metadata.HostModuleResp, err error) { resp = new(metadata.HostModuleResp) subPath := "/hosts/modules/read" err = a.client.Post(). WithContext(ctx). Body(params). SubResourcef(subPath). WithHeaders(h). Do(). IntoCmdbResp(resp) return }

该函数声明在 src/apimachinery/apiserver/apiserver.go 的apiServer接口中,返回类型就是上一节提到的*metadata.HostModuleResp。

6.2 分批调用的工程实践:Excel 导出场景

由于单次bk_host_id最多 500 个,仓库内 src/web_server/service/excel/core/topo_func.go 在导出拓扑主机数据时给出了标准的分批处理模式:把一批主机 ID 按每 500 个切分,逐批调用GetHostModuleRelation,再把每条关系的HostID映射到AppID,从而构造“主机 → 业务”的映射表:

hostBizMap := make(map[int64]int64) // the length of GetHostModuleRelation's param bk_host_id can't bigger than 500 for idx := 0; idx < hostLen; idx += 500 { endIdx := idx + 500 if endIdx > hostLen { endIdx = hostLen } ids := hostIDs[idx:endIdx] params := mapstr.MapStr{ common.BKHostIDField: ids, } resp, err := d.ApiClient.GetHostModuleRelation(kit.Ctx, kit.Header, params) ... for _, relation := range resp.Data { hostBizMap[relation.HostID] = relation.AppID } }

当主机数量较大时,务必在业务代码中按 500 分批,否则会触发入口参数校验错误;同时建议对分批调用做错误与超时处理,避免单批失败导致全量导出中断。

6.3 前端调用

前端 Vue 项目中,主机与模块关系的相关请求统一封装在 src/ui/src/store/modules/api/host-relation.js 的hostRelationstore 中,通过$http.post('hosts/...', params, config)与 web_server 交互(如hosts/modules批量转移、hosts/modules/fault故障机模块等)。find_host_biz_relations更常见的落地方式是后端服务或 ESB 脚本直接调用,用于批量确定主机归属。

七、常见错误与排查建议

现象可能原因处理方式
返回code: 1199089(CCErrArrayLengthWrong)bk_host_id为空数组或超过 500 个检查数组长度,按 500 分批重试
返回code: 1199035(CCErrCommXXExceedLimit)内部链路中bk_host_ids/bk_module_ids超限对应接口分别控制主机 ≤500、模块 ≤500、集群 ≤200
返回code: 1199005(CCErrCommHTTPBodyEmpty)未传任何过滤条件(如仅传空数组)至少传入非空的bk_host_id
返回的data比预期条数多一台主机挂载在多个模块/集群,或跨多个业务结合bk_biz_id过滤,或按bk_set_id/bk_module_id聚合去重
解析data时某些字段缺失ModuleHost结构体带omitempty,空值字段被省略按可选字段解析,先确认bk_host_id存在再取其他字段

八、总结

find_host_biz_relations是 bk-cmdb 中“以主机为入口、向上追溯业务归属”的标准查询接口:ESB 路径/v2/cc/find_host_biz_relations/经 apigateway 转发到POST /api/v3/hosts/modules/read,由 host_server 的GetHostModuleRelation处理器完成参数校验(bk_host_id1~500 个),再经 coreservice 查询cc_ModuleHostConfig表,返回主机与业务/集群/模块的平铺挂载关系。开发者在集成时只需记住三点:必须用 POST、主机 ID 必须走数组且每批不超过 500、返回的每条记录是一次模块挂载而非一台主机,即可在 ESB 编排、Go 后端或前端数据链路中稳定复用这一能力。

  • 后端
  • 企业应用
  • 运维

【免费下载链接】bk-cmdb

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

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

相关推荐

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进