WuKongIM 只读 DB 检查(DB Inspect)管理控制台:从 /system/db 页面到 pkg/db/inspect 的完整实现指南 即时通讯后端【免费下载链接】WuKongIMMore than just IM 不只是即时通讯(IM)项目地址https://gitcode.com/gh_mirrors/wu/WuKongIM点击查看免费下载本指南以 WuKongIM 仓库内 DB Inspect 实施计划 为主体结合pkg/db/inspect、internal/access/manager、internal/access/node、internal/usecase/management、internal/app与web/src的真实源码完整讲解 WuKongIM 如何为管理端Web Manager提供一套只读的「DB 检查」控制台从浏览器发出受限 SQL、经 HTTP 鉴权与权限校验、再到节点本地只读存储引擎扫描的整条调用链。读完本文你将掌握该功能的架构分层、SQL 子集语法、五种扫描模式、HTTP 路由与错误映射、跨节点 RPC 编解码以及 Web 页面与 API Client 的接入方式。功能定位为运行态数据库提供「只读放大镜」WuKongIM 在长期运行中slotmeta元数据与messages消息两套基于 Pebble 的本地存储是排障、容量评估与数据治理的关键对象。DB Inspect 功能的定位非常明确在管理端 Web 页面/system/db提供一个只读的数据库检查控制台底层由pkg/db/inspect提供受限 SQL 子集的解释执行能力直接以只读方式打开节点本地存储不引入任何写操作、不触发 repair/compaction、不做跨集群合并查询——它只回答「某个节点本地数据库里现在有什么」这一类诊断问题。实施计划中反复强调的三条硬性约束也全部体现在最终代码里全部只读pkg/db/inspect的OpenStore以engine.Options{ReadOnly: true}打开存储不接受文件系统路径HTTP/Web 请求只能传node_id与查询文本存储路径由internal/app从Config.DataDir推导不新增WK_配置键所有参数如 HashSlotCount从既有集群配置派生。总体架构浏览器只访问 Manager HTTP从实施计划的 Architecture 一节可以看出整个功能是四层职责分离的浏览器Web DB Inspect 页面 - Manager HTTP 路由internal/access/manager 解析 HTTP、鉴权、权限校验、DTO 转换 - Management Usecaseinternal/usecase/management 决定本地执行还是远程转发 - 本地internal/app 推导存储路径并适配 pkg/db/inspect - 远程internal/access/node 通过 clusterv2 RPC 转发到目标节点 - 目标节点 internal/app 的 dbInspectReader - 目标节点本地 pkg/db/inspect Store核心原则是浏览器永远只和 Manager HTTP 打交道跨节点查询由 Manager 侧在集群 RPC 层完成用户无需关心节点网络细节。技术栈层次技术HTTP 服务Go Gininternal/access/manager集群节点 RPCclusterv2 节点 RPCinternal/access/node只读检查引擎pkg/db/inspectWeb 管理端React TypeScript React Router React Intl前端测试Vitest沿用现有 Web manager shell核心数据契约DTO 与统计信息管理端 usecase 层定义了一组 JSON 友好的 DTO见 internal/usecase/management/db_inspect.go。请求结构DBInspectQueryRequest字段类型说明NodeIDuint64选择要检查的节点本地 DB0表示本地节点Querystring只读检查 SQL 语句响应结构DBInspectQueryResponse字段类型说明NodeIDuint64实际执行查询的节点GeneratedAttime.Time结果产生时间Rows[]DBInspectRow动态结果行map[string]anyStatsDBInspectStats扫描工作量与分页状态汇总统计信息DBInspectStats是理解该功能诊断价值的关键字段类型含义ScanModestringpkg/db/inspect使用的扫描策略名称ScannedHashSlots[]uint16扫描触及的元数据哈希槽列表ScannedRowsint扫描过程中解码并考虑过的行数ReturnedRowsint实际返回给调用方的行数HasMorebool是否还有下一页NextCursorstring当HasMore为真时用于续扫下一页的游标这套统计设计让运维人员能直接判断查询的成本扫描了多少哈希槽、看了多少行、返回了多少行从而识别全槽扫描local-bounded这类昂贵查询。pkg/db/inspect受限 SQL 子集的只读检查引擎pkg/db/inspect是整个功能的心脏由 types.go、parser.go、planner.go、execute.go、cursor.go 组成。支持的三种查询形态QueryKind定义了检查引擎能处理的全部操作const ( QueryShowTables QueryKind iota 1 // show tables列出可检查的表 QueryDescribe // describe/desc描述某张表的列 QuerySelect // select从某张可检查表扫描行 )对应语法show tables—— 列出meta.*与message.*域下全部可检查表describe meta.user或desc—— 输出指定表的列元数据select ... from ... [where ...] [limit N] [cursor ...]—— 受限行扫描。词法与语法边界解析器实现了一个小型 lexer见 parser.go支持的关键字子集仅包括show / tables / describe / desc / select / from / where / and / limit / cursor字面量支持单引号字符串与十进制整数。明确不支持并返回ErrUnsupportedQuery的语法包括join、offsetgroup by、order by函数、子查询、多表查询等超出子集的内容一律返回ErrInvalidQuery。LIMIT的取值还受边界约束normalizeLimit会以DefaultLimit缺省时默认100兜底、以MaxLimit缺省时默认10000封顶常量定义见 store.go。也就是说忘写limit的查询最多返回 100 行写再大的 limit 也只会返回 10000 行——这是有界只读bounded read-only的直接体现。表目录与五种扫描模式规划器planner.go先校验表是否存在inspectTableExists再根据表与过滤条件选择扫描策略。元数据域的可检查表及分区键注册在partitionKeys表分区键meta.useruidmeta.deviceuidmeta.channelchannel_idmeta.channel_runtime_metachannel_idmeta.subscriberchannel_idmeta.user_channel_membershipuidmeta.user_cmd_channel_membershipuidmeta.plugin_bindinguidmeta.channel_migrationchannel_idmessage.messagechannel_keyplanMetaQuery与planMessageQuery共同决定了五种扫描模式ScanMode扫描模式常量触发条件point-partitionscanModePointPartition元数据表带分区键等值过滤如where uidu1可精确定位单个哈希槽explicit-partitionscanModeExplicitPartition显式指定where hash_slotNlocal-boundedscanModeLocalBounded元数据表无分区过滤需遍历全部哈希槽成本最高受HashSlotCount约束message-channelscanModeMessageChannel消息表message.message带channel_key过滤message-catalogscanModeMessageCatalog消息域目录表message.channels需要特别说明ErrHashSlotRequired当元数据表需要按分区键定位或全槽扫描而Options.HashSlotCount为 0未配置槽数时规划器会拒绝执行。这正是存储路径与槽数由 app 层推导、调用方不可注入这一设计约束的体现。执行与结果投影Store.Queryexecute.go按解析出的 Kind 分发showTables()枚举metadb.InspectTables()并追加message.channels、message.message两行describe()输出列元数据select则进入executePlan把plan转换为metadb.InspectScanRequest交给元数据/消息存储的只读扫描接口最后用projectRows做列投影并汇总Stats。游标分页分页通过cursor xxx语法续扫响应里的NextCursor拼回查询即可取下一页Web 页面用appendCursor把query cursor abc拼接。游标与请求的扫描模式、表、分区槽严格绑定validateCursorForPlan任何不匹配都会返回ErrCursorMismatch从机制上杜绝跨扫描翻页造成的错乱。Management Usecase本地 vs 远程的路由决策internal/usecase/management/db_inspect.go 定义了三个对外方法ListDBInspectTables(ctx, nodeID)—— 等价于QueryDBInspect(..., show tables)DescribeDBInspectTable(ctx, nodeID, domain, table)—— 对domain、table做白名单校验validDBInspectIdentifier只允许字母/数字/下划线杜绝路径穿越后拼接describe domain . tableQueryDBInspect(ctx, req)—— 通用入口。路由核心是resolveDBInspectNodelocalID : a.cluster.NodeID() if nodeID 0 || nodeID localID { return localID, true, nil // 本地执行 } snapshot, err : a.cluster.LocalControlSnapshot(ctx) // 在控制快照中查找 nodeID // 找到 - 远程执行未找到 - metadb.ErrInvalidArgument即node_id为空或等于本节点 → 走本地DBInspectReader否则先在集群控制快照里确认节点存在再走RemoteDBInspectReaderNodeDBInspectQuery。未知节点、空查询、非法表名都会返回metadb.ErrInvalidArgument未装配读取器时返回ErrDBInspectUnavailable。这一路由逻辑在 db_inspect_test.go 中有完整覆盖本地空节点、显式本地节点、远程节点、非法请求、不可用五类场景。App 层存储路径推导与只读 Store 适配internal/app/db_inspect.go 的dbInspectReader是 app 组合根提供的本地读取器职责是把「配置路径」翻译成「inspect 引擎选项」store, err : inspect.OpenStore(inspect.Options{ MetaPath: filepath.Join(r.dataDir, slotmeta), MessagePath: filepath.Join(r.dataDir, messages), HashSlotCount: r.hashSlotCount, })路径固定为DataDir/slotmeta与DataDir/messages——调用方永远无法注入任意路径。装配发生在 internal/app/wiring.go 的newDBInspectReader与wireManagerDBInspectRPCHashSlotCount从cfg.Cluster.Slots.HashSlotCount读取为 0 时默认16。数据目录为空时直接返回 nil随后 usecase 层会以ErrDBInspectUnavailable兜底。Manager HTTP 路由与权限路由注册在 internal/access/manager/server.godbInspect : s.engine.Group(/manager) if s.auth.enabled() { dbInspect.Use(s.requirePermission(cluster.db, r)) } dbInspect.GET(/db/inspect/tables, s.handleDBInspectTables) dbInspect.GET(/db/inspect/tables/:domain/:table, s.handleDBInspectTable) dbInspect.POST(/db/inspect/query, s.handleDBInspectQuery)三个端点全部挂在/manager前缀下且当鉴权开启时要求cluster.db资源的r读权限——这也是 Web 端权限模型里新增的最小权限面。方法路径说明GET/manager/db/inspect/tables列出可检查表可选?node_idGET/manager/db/inspect/tables/:domain/:table描述某张表可选?node_idPOST/manager/db/inspect/query执行只读查询Body 为{node_id:2,query:show tables}HTTP DTOinternal/access/manager/db_inspect.go与 usecase DTO 一一对应node_id解析允许为空即本地节点。错误映射writeDBInspectError把底层错误翻译成稳定的 HTTP 语义底层错误HTTP 状态错误码dbinspect.ErrCursorMismatch400invalid_cursordbinspect.ErrInvalidQuery/ErrUnsupportedQuery/metadb.ErrInvalidArgument400invalid_requestmanagementusecase.ErrDBInspectUnavailable503service_unavailable其他500internal_error跨节点 RPC把只读查询送到目标节点远程查询依赖 clusterv2 节点 RPC服务 ID 为RPCManagerDBInspect见pkg/cluster/net/ids.go。确定性版本化编解码internal/access/node/manager_db_inspect_codec.go 定义了带 magic 前缀的 JSON 信封请求前缀W K V I 1响应前缀W K V i 1大小写区分方向请求体{node_id:2,query:show tables}响应体{status:ok,page:{...}}page即完整DBInspectQueryResponse。magic 前缀保证任何非本服务编解码的 payload 都会被拒绝避免 RPC 服务间串扰。状态映射internal/access/node/manager_db_inspect_rpc.go 定义了服务端与客户端双向状态映射服务端HandleManagerDBInspectRPC解码 → 检查适配器是否装配未装配返回unavailable→ 调用本地读取器 → 用managerDBInspectRPCStatusForError把错误归类为ok / invalid_request / invalid_cursor / unavailable / rejected / canceled / deadline客户端NodeDBInspectQuery把状态码还原为 Go 错误其中invalid_request、invalid_cursor、unavailable、rejected统一归并为ErrDBInspectUnavailable上下文取消/超时则还原为context.Canceled/context.DeadlineExceeded。接收节点始终只检查自己的本地 DB不会二次路由转发也不会修改任何存储。infra 适配器internal/infra/cluster/management_db_inspect.go 的ManagementDBInspectReader是集群侧适配器它持有ManagementDBInspectNode暴露NodeID()与CallRPC内部用accessnode.NewClient(node)把NodeDBInspectQuery变成一次CallRPC(targetNode, RPCManagerDBInspect, body)调用。HTTP 解析、查询校验、表元数据整形、游标处理与存储路径推导全部留在 infra 适配器之外保持单一职责。Web 管理端API Client、导航与页面API Client 与类型web/src/lib/manager-api.ts新增三个函数全部走统一的jsonManagerFetchexport function getDBInspectTables(params?: { nodeId?: number }) export function getDBInspectTable(domain: string, table: string, params?: { nodeId?: number }) export function queryDBInspect(input: ManagerDBInspectQueryInput)对应类型web/src/lib/manager-api.types.tsManagerDBInspectRow、ManagerDBInspectStats、ManagerDBInspectQueryResponse等JSON 字段node_id、scan_mode、next_cursor等与后端 DTO 完全对齐。导航与路由导航web/src/lib/navigation.tsSystem 分组新增/system/db项图标 Database并登记旧路径别名/db-inspect路由web/src/app/router.tsxSystem 子路由system/db渲染DBInspectPage同时保留db-inspect → /system/db的 301 式重定向国际化web/src/i18n/messages/en.ts与zh-CN.ts新增整套文案导航标题、页面标题、按钮、错误提示中文文案如dbInspect.title: DB 检查、dbInspect.query.run: 执行查询。页面交互web/src/pages/db-inspect/page.tsx实现完整交互表列表加载节点列表与show tables结果按 domain 分组展示meta.*/message.*点击某表触发describe右侧展示列名与类型查询区节点下拉框默认选中is_local节点 查询输入框 6 条内置模板show tables、describe meta.user、select * from meta.user where uidu1 limit 20、select * from meta.channel where channel_idg1 limit 20、select * from message.channels limit 50、select * from message.message where channel_keyg1:2 limit 20结果区动态列宽表格rowColumns从结果行推导列renderCell处理 null/数组/对象顶部StatsStrip展示scan_mode、scanned/returned 行数、哈希槽列表分页has_more next_cursor时出现「Next page」按钮调用appendCursor续扫错误处理403 → forbidden、503 → unavailable 分别渲染查询失败时保留输入框中的查询文本方便修改重试。页面行为由web/src/pages/db-inspect/page.test.tsx覆盖加载表列表并执行查询、描述选中表、游标翻页、失败保留文本四大场景。测试与验证体系实施计划把每一层都配上了独立可运行的测试仓库中这些测试均已落地层次测试命令覆盖点usecase 路由go test ./internal/usecase/management -run TestDBInspect本地/远程路由、非法请求、不可用、便捷查询app 读取器go test ./internal/app -run TestDBInspectReader存储路径推导、只读扫描、缺 DataDir 时不可用manager HTTPgo test ./internal/access/manager -run TestDBInspect权限403、成功、校验400、不可用503节点 RPCgo test ./internal/access/node -run TestManagerDBInspect编解码往返、服务端/客户端、状态映射infra 适配器go test ./internal/infra/cluster -run TestManagementDBInspectReader路由到目标节点 RPCWebcd web bun run test -- src/pages/db-inspect/page.test.tsx src/lib/manager-api.test.ts src/lib/navigation.test.ts src/app/router.test.tsx页面行为、API 路径/请求体、导航元数据、路由覆盖综合go test ./internal/usecase/management ./internal/access/manager ./internal/access/node ./internal/infra/cluster ./internal/app ./pkg/db/inspect全链路回归设计约束与安全边界小结回顾整个实现DB Inspect 的安全与边界设计可以总结为五条只读引擎pkg/db/inspect以 ReadOnly 打开存储无任何写路径无路径注入存储路径只能由 app 层从DataDir推导HTTP/Web 永远无法传入文件系统路径受限 SQL解析器只接受白名单子集group by / order by / join / offset等一律拒绝有界扫描默认LIMIT 100、上限10000统计信息暴露扫描成本最小权限cluster.db:r独立于节点管理权限鉴权开启时未授权用户返回 403。这套设计让运维人员可以安全地看一眼任意节点的本地数据库而不会触碰集群写入路径——这正是 WuKongIM 管理面Manager与数据面解耦的又一次落地实践。赞分享即时通讯后端【免费下载链接】WuKongIMMore than just IM 不只是即时通讯(IM)项目地址https://gitcode.com/gh_mirrors/wu/WuKongIM点击查看免费下载相关推荐终极指南如何构建Pinterest高并发时序数据库Goku系统终极指南如何构建Pinterest高并发时序数据库Goku系统 system design resources项目是互联网上最优质的系统设计资源集合其中包含教程Pikiwidb 多 DBMulti-DB管理实战从 info keyspace 到 dbslaveof 的命令演进与源码解读Pikiwidb 多 DBMulti DB管理实战从 info keyspace 到 dbslaveof 的命令演进与源码解读 自 3.1.0 版本起P数据库KV存储后端Yii 2 分页Pagination完全指南从 DB 查询分页到 LinkPager 分页按钮Yii 2 分页Pagination完全指南从 DB 查询分页到 LinkPager 分页按钮 本指南围绕 Yii 2 框架的 yii\data\Pagi后端Web框架上一篇TegraRcmGUI完整指南从零开始掌握Switch系统注入的终极教程下一篇告别排版噩梦用Chinese-ERJ模板5分钟搞定《经济研究》期刊论文格式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考