架构设计
5 层架构 单向无环依赖 700+ 指纹本文用图解方式说明 IconHash Skills 的整体架构、分层职责与数据流。
阅读建议
如果你是第一次接触本项目,建议按顺序通读各小节;如果只关心某条命令的内部实现,可直接跳到 核心数据流:identify 命令。需要快速理解模块边界时,先看 整体分层 的 flowchart。
整体分层
IconHash Skills 采用清晰的 5 层架构,从用户入口到外部接口逐层下沉:
| 层级 | 名称 | 职责 | 关键组件 |
|---|---|---|---|
| ① | 入口层 | 接收用户/外部请求 | CLI、HTTP API、MCP、GUI |
| ② | 命令层 | 解析参数、编排子命令 | cmd/*.go |
| ③ | 核心包 | 哈希、匹配、协议、资源 | pkg/hasher、pkg/fingerprint 等 |
| ④ | 基础设施 | 通用能力 | MurmurHash3、HTTP 客户端、embed |
| ⑤ | 外部接口 | 真实世界对接 | 目标网站、搜索引擎、AI 代理 |
各层职责详解
- 入口层:不处理业务逻辑,只负责把请求翻译成统一的命令调用,确保 CLI / API / MCP / GUI 四条入口路径行为一致。
- 命令层:使用
cobra的子命令模式,每个子命令只编排「发现 → 哈希 → 匹配 → 格式化」这条主线,不直接实现算法。 - 核心包:所有业务逻辑下沉到此层,
pkg/*之间保持单向依赖,便于独立单测。 - 基础设施:提供与具体业务无关的通用能力,例如
MurmurHash3的封装、embed.FS嵌入指纹库等。 - 外部接口:仅在最外层被引用,核心包通过接口抽象与之交互,方便在测试中替换为 mock。
包依赖关系
核心代码包之间的依赖方向(单向无环):
依赖始终从上层指向下层,
pkg/*之间不产生循环依赖,便于独立测试与复用。
依赖红线
新增任何 pkg/* 之间的依赖时,必须保证不形成环。pkg/hasher 与 pkg/fingerprint 共同依赖 pkg/util,但 util 绝不反向引用上层包——这是项目可测试性的根基。
核心数据流:identify 命令
iconhash identify <url> 是最典型的端到端流程,融合了发现、哈希、匹配三个阶段:
三阶段解耦的价值
discover → hash → lookup 三步被刻意拆分到不同的 pkg/* 中,而非塞进一个长函数。这样 pkg/hasher 可以脱离网络独立测试,pkg/fingerprint 可以用静态哈希值做匹配回归,而 discover 的探测策略也能单独演进(例如未来支持 SVG 站点图标)。
为什么会对每个图标都查一次指纹库?
站点可能同时声明多个 <link rel="icon">(不同尺寸/格式)。逐图标哈希 + 查询可以覆盖所有候选,避免遗漏真实身份。最终结果会去重合并,用户拿到的就是该站点所有可识别图标对应的指纹集合。
哈希计算细节
MMH3 哈希的计算步骤与历史渊源——这是 Shodan/Fofa 等搜索引擎沿用的事实标准:
事实标准 int32 Base64 前置为什么先 Base64? 沿用早期 Python 实现的惯例,Shodan 等引擎索引也基于此约定——保持一致才能与现有搜索结果互通。
哈希值符号陷阱
默认输出是 带符号 int32,因此可能出现负数(例如 -1161367058)。这是 MurmurHash3 取低 32 位后的有符号表示,并非错误。若你的下游系统要求无符号值,请使用 --uint32 输出(对应 3133600238),二者互为补码关系。
图标发现策略
发现器按优先级顺序探测网站上的所有图标:
多源探测 自动去重探测顺序背后的考量
顺序是 link 标签 → apple-touch-icon → /favicon.ico,因为前者的图标通常更能代表站点真实身份(自定义 logo),而 /favicon.ico 作为兜底往往只是默认占位图。优先识别自定义图标能提高指纹命中率。
当三个来源都没有图标时
/favicon.ico 返回非 200(如 404)会被判定为无图标,发现器返回空列表。此时 identify 不会报错,而是输出空结果——这是有意设计,避免因单个站点缺少图标而中断批量扫描。
Lite vs Full 构建变体
两种构建变体在指纹库加载方式上不同,影响二进制体积与离线能力:
Lite 默认 Full 离线 embed_fingerprints tag| 特性 | Lite | Full |
|---|---|---|
| 二进制体积 | ~5 MB | ~8 MB |
| 首次需联网 | 是(自动下载) | 否 |
| 离线可用 | 否(首次后是) | 是 |
| 指纹更新 | 自动拉取最新 | 需重新编译 |
| 适用场景 | 日常使用、Docker | 离线环境、内网 |
如何选择?
- 日常开发、CI、Docker 镜像 → 用默认 Lite,体积小、指纹永远最新。
- 内网 / 离线 / 安全合规环境 → 编译时加
-tags embed_fingerprints生成 Full,启动即用、不依赖外网。
Lite 首次联网的注意点
Lite 首次查询指纹时会从 GitHub 拉取 fingerprints.json 并缓存到本地。若运行环境无法访问 GitHub(如受限网络),首次查询会失败——此时要么切换到 Full 构建,要么提前手动放置缓存文件。详见 构建与发布。
API 服务器请求生命周期
HTTP API 服务器处理一个请求的完整路径:
中间件链 认证网关 统一 JSON 响应公开端点 vs 受保护端点
认证中间件按路由白名单放行:健康检查、文档等公开端点无需鉴权直接通过;而 /hash/*、/lookup/* 等受保护端点必须携带有效的 Authorization 或 token。这样既方便探活,又不暴露核心能力。
401 与 200 之外的返回码
400 Bad Request:参数缺失或 URL 格式非法。429 Too Many Requests:触发限流(若启用)。502 Bad Gateway:下载目标图标失败(上游站点不可达或超时)。500 Internal Server Error:哈希/匹配阶段出现非预期异常,会带 error_id 便于排查。
目录结构
各核心包一句话职责
| 包 | 职责 |
|---|---|
pkg/hasher | 下载图标、Base64 编码、MurmurHash3 计算 |
pkg/fingerprint | 指纹库加载与哈希反查 |
pkg/api | HTTP 路由、中间件、端点处理器 |
pkg/mcp | MCP 协议封装与工具暴露 |
pkg/gui | 内嵌 GUI 静态资源(embed.FS) |
pkg/util | 搜索语法格式化等纯函数工具 |
pkg/version | 版本号注入与查询 |
详细目录说明见 构建与发布。
速记口诀
入口多、命令薄、核心厚、基础稳、外部远。——记住这五个词,就记住了 IconHash Skills 的分层骨架。
下一篇:集成方式 — 4 种集成路径如何选择。