- 数据库客户端
- 数据库
- 桌面应用
- CLI
- 后端
- MCP 服务
- AI 应用
【免费下载链接】dbx
25 MB lightweight cross-platform database client for 90+ databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. Built-in AI, MCP Server, CLI, desktop and Docker. | 轻量级跨平台数据库管理工具,支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、达梦等 90+ 数据库,提供桌面端、Docker、CLI、内置 AI 助手和 MCP Server。
插件市场(Marketplace)是 dbx 桌面端分发插件的重要渠道,但真实的下载与安装数据此前一直没有可靠的统计手段。deploy/plugin-stats-worker/下的 Cloudflare Worker 解决了这个问题:它在不触碰.dbxp制品字节流的前提下,对每一次真实的插件下载(dl)、全新安装(inst)与版本更新(updt)进行计数,通过 Workers Analytics Engine 完成近乎免费的采集,再以每日 cron 聚合到 KV 命名空间形成永久归档。读完本文,你将掌握这套从采集、存储、聚合到查询的完整统计管线,以及它在 桌面端安装信标 一侧的接入方式与全部配置细节。
一、整体架构:一次请求两条路
该 Worker 部署在 dbxio.com 站点所属的 Cloudflare 账户下,核心设计原则是"计数与流量转发解耦"——统计逻辑只做轻量记录,绝不改写或阻塞真实流量。整体架构可概括为:
- 下载计数:
dl.dbxio.com/plugins/*路由拦截每一个.dbxp制品的GET请求,记录事件后原样透传给 R2 自定义域名,字节、请求头与 Range 语义均不被改动; - 安装信标:
dbxio.com/api/plugins/install接收桌面端在安装成功后发送的 fire-and-forget POST 信标,返回 204 即完成使命; - 手动归档触发:
dbxio.com/api/plugins/archive提供 token 门控的手动聚合入口,用于验证与历史回填; - 存储分层:实时事件写入 Workers Analytics Engine(绑定
PLUGIN_STATS),每日 cron(30 0 * * *UTC)将增量窗口聚合进plugin_stats_archiveKV 命名空间,形成永久层。
从源码看,路由分派逻辑集中在 worker.ts 的fetch入口:按url.hostname判断,dl.dbxio.com走下载计数路径,其余(即dbxio.com的 api 前缀)走handleInstallBeacon;scheduledhandler 则承载 cron 触发的聚合任务。
二、下载计数路由:只数.dbxp,透传其余
2.1 为什么只统计.dbxp制品
插件市场的页面与应用视图会频繁拉取图标等静态资源,如果把这些请求也算进下载量,既会迅速烧穿 KV 配额(KV 免费额度每日仅 1000 次写入),又会严重虚高下载数字。因此该路由严格限定统计对象为真实制品文件。
const DOWNLOAD_PATTERN = /^\/plugins\/([A-Za-z0-9._-]{1,64})\/([0-9A-Za-z.+-]{1,32})\//; const ARTIFACT_SUFFIX_PATTERN = /\.dbxp$/;其中DOWNLOAD_PATTERN同时解析出插件 id(第 1 组捕获)与版本号(第 2 组捕获),并对其做了长度与字符集约束(插件 id 最长 64 字符,版本最长 32 字符),ARTIFACT_SUFFIX_PATTERN确保只有以.dbxp结尾的路径才会计数。对应处理逻辑见 worker.ts:先匹配路径,再校验后缀,通过后调用recordEvent(env, "dl", pluginId, version, identity)写入事件,随后return fetch(request)将原始请求转发到 R2 自定义域名。同 zone 的子请求不会重新进入 Workers,因此该透传不会产生循环。
2.2 下载者的匿名身份
dl事件没有客户端生成的 id,其"独立下载者"统计依赖服务端计算的一天级 IP HMAC。实现见ipDayIdentity(worker.ts):以CF-Connecting-IP、当前 UTC 日期与STATS_SALT秘钥做 HMAC-SHA256,截取前 8 字节输出十六进制字符串。这样聚合端能按"身份"统计独立下载者,却无需存储任何明文 IP;由于盐值仅存在于 Worker 环境变量,外部无法通过枚举 IP 反推出真实值。源码注释也明确说明这是"防刷水位的弱约束"——攻击者更换 IP 仍可能虚增,对装饰性统计而言是可接受的。
三、安装信标路由:204 即完成的异步上报
桌面端在插件安装成功后,会向dbxio.com/api/plugins/install发送一个 fire-and-forget 的 POST 信标,请求体为:
{ "id": "<plugin id>", "version": "<version>", "kind": "install" | "update", "clientId": "<random uuid>" }其中kind与clientId为可选字段——旧版本客户端不发送它们。该接口是纯粹的装饰性统计:无鉴权、不采集 PII,clientId是应用本地生成的随机安装 id,既非硬件指纹也非用户指纹。
服务端校验逻辑(worker.ts)值得展开:
- CORS 预检:
OPTIONS直接返回 204,响应头允许POST, OPTIONS方法与Content-Type头,Access-Control-Max-Age: 86400让浏览器缓存预检结果一天; - 体积上限:
Content-Length超过 512 字节返回 413,避免信标被滥用于投放大负载; - 字段校验:
id必须匹配PLUGIN_ID_PATTERN、version必须匹配VERSION_PATTERN,否则 400; - 事件归类:
kind === "update"记为updt,其余一切取值(包括旧版本缺省该字段的情况)都记为inst——这样历史混合事件基数保持连续,直到整个客户端舰队升级完毕; - 身份回退:
clientId若符合 UUID 格式则直接使用;否则(旧版本或本地存储不可用)回退到前述一天级 IP HMAC; - 成功语义:写入 Analytics Engine 后返回 204 空响应。
四、桌面端接入:一次 keepalive 的 fetch
统计信标的发送方在桌面端 pluginMarketplace.ts:
const INSTALL_BEACON_URL = "https://dbxio.com/api/plugins/install"; const INSTALLATION_ID_STORAGE_KEY = "dbx-installation-id"; export function beaconPluginInstall(pluginId: string, version: string, kind: PluginInstallBeaconKind = "install"): void { try { void fetch(INSTALL_BEACON_URL, { method: "POST", headers: { "Content-Type": "text/plain" }, body: JSON.stringify({ id: pluginId, version, kind, clientId: installationClientId() }), keepalive: true, }).catch(() => undefined); } catch { // Statistics are best-effort. } }几个工程细节:
- fire-and-forget:
void fetch(...)不 await,任何异常(网络失败、服务器 5xx)都被静默吞掉,统计失败绝不影响安装流程本身; - keepalive: true:保证页面/应用在即将关闭时信标仍能发出;
- 本地随机 id 复用:
installationClientId()先从localStorage读取dbx-installation-id,不存在或格式非法(非 UUID)则用uuid()重新生成并落盘,后续信标复用同一 id,从而在服务端形成"每台机器一个稳定身份"。清除本地存储或重装应用会重新生成——对装饰性统计是可接受的; - 更新与安装分离:默认
kind为install,升级场景显式传"update",避免更新流量虚增安装数字。
对应的单元测试 pluginMarketplaceBeacon.spec.ts 覆盖了五个关键行为:默认发送installkind、updatekind 透传、uuid 生成一次后跨信标复用、损坏的存储 id 会被重新生成、存储不可用时发送空clientId。这些测试从客户端侧印证了 Worker 侧kind/clientId可选字段设计的兼容性考量。
五、存储设计:为什么弃用 KV 计数器,改用 Analytics Engine
每个事件在 Analytics Engine(绑定PLUGIN_STATS)中写入一个数据点,字段结构为:
| 字段 | 值 | 说明 |
|---|---|---|
blobs | [kind, pluginId, version, identity] | kind为dl(制品 GET)/inst(全新安装,旧版本无 kind 的信标也归入此类,保持历史混合事件基数连续)/updt(版本更新);identity在有clientId时取客户端随机安装 id(每台机器稳定唯一),否则用一天级 IP HMAC(所有dl事件均使用后者) |
doubles | [1] | 事件计数 |
indexes | [pluginId] | 以插件 id 为索引,保证按插件的查询分片良好 |
选择 Analytics Engine 而非 KV 计数器的理由在 README 与源码注释中均有明确交代:
- KV 的 read-modify-write 模式每请求消耗 1 次写入,而免费额度仅每日 1000 次写入,远低于 3 万用户基数在热门插件发布日的流量——2026-09-15 当天仅图标流量就在数小时内烧穿了配额;
- Analytics Engine 在此规模下写入近乎免费,且写入为追加式,无需先读后写,天然适配高并发事件流。
对应的绑定配置位于 wrangler.json:analytics_engine_datasets将绑定名PLUGIN_STATS映射到数据集名DBX_PLUGIN_STATS(SQL 查询时使用数据集名而非绑定名),kv_namespaces将PLUGIN_ARCHIVE指向 KV 命名空间。
六、每日聚合:从三个月窗口到永久归档
Analytics Engine 仅保留三个月的数据,因此需要一个 cron 触发器把滚动窗口的数据沉淀到永久层。cron 表达式为30 0 * * *(UTC 每日 00:30),见 wrangler.json;触发逻辑在scheduledhandler 中通过ctx.waitUntil(runAggregation(env))异步执行。
6.1 KV 归档的键设计
聚合结果写入plugin_stats_archiveKV 命名空间,键结构如下:
| 键 | 内容 | 写入语义 |
|---|---|---|
total:{kind}:{pluginId}:{version} | 按插件、按版本的全时段累计事件数 | 累加 |
total:{kind}:{pluginId} | 按插件的全时段累计事件数(跨版本汇总) | 累加 |
summary | 单个 JSON 块{dl: {pluginId: n}, inst: {...}, updt: {...}},供单次读取展示 | 合并后覆盖 |
uniqd:{kind}:{pluginId}:{day} | 按天的独立身份数(分类 inst/updt 事件为机器数,dl 事件为独立下载者数) | 覆盖(不是累加) |
meta:last-success | 窗口标记 | 所有写入落盘后才推进 |
6.2 窗口推进与失败恢复
runAggregation(worker.ts)的核心语义是增量窗口:聚合(meta:last-success, now]之间的数据,meta:last-success仅在全部写入成功后更新。这意味着:
- 某次 cron 运行失败时,下次运行会重试同一窗口,不会丢数据;
- 写入中途崩溃可能造成窗口数据被重复计数——对装饰性统计是可接受的;
- 支持
x-archive-from: reset头触发从 epoch 重新聚合(用于修复后回填),但由于总量是累加语义,仅应在确认归档窗口为空时使用; meta:last-success损坏时直接抛错中止,避免静默错位。
6.3 独立身份的天级去重
天级独立数的查询实现(fetchDailyUniques,worker.ts)有个值得注意的细节:Analytics Engine 的 SQL 方言拒绝在GROUP BY中使用函数表达式,因此查询先按toStartOfHour(timestamp)分小时桶做count(DISTINCT blob4),再在 JS 侧合并成天级值。注释明确承认这种跨小时边界的身份会在两个桶中各计一次、略微高估天级独立数——对装饰性统计可接受,且天然免疫"同一天发布多个版本"导致的虚增。此外,2026-09-19 之前写入的旧事件没有 blob4 字段,会被blob4 != ''条件排除在独立数之外,但原始计数仍覆盖它们。
七、部署与配置:wrangler 实战
7.1 完整配置清单
wrangler.json 的完整配置如下:
{ "name": "dbx-plugin-stats", "compatibility_date": "2026-05-07", "main": "worker.ts", "routes": [ { "pattern": "dl.dbxio.com/plugins/*", "zone_name": "dbxio.com" }, { "pattern": "dbxio.com/api/plugins/*", "zone_name": "dbxio.com" } ], "analytics_engine_datasets": [ { "binding": "PLUGIN_STATS", "dataset": "DBX_PLUGIN_STATS" } ], "kv_namespaces": [ { "binding": "PLUGIN_ARCHIVE", "id": "0cc9e49a117f4edeb931464fa0256834" } ], "vars": { "CF_ACCOUNT_ID": "7ad3349ede62c17968a90108d39fd4d0" }, "triggers": { "crons": ["30 0 * * *"] } }其中CF_ACCOUNT_ID与 KV 命名空间 id 由 wrangler.json 直接给出,部署时需替换为你自己账户下的实际值。
7.2 部署与密钥
部署命令:在该目录下执行npx wrangler deploy(需 OAuth 登录)。聚合流程访问 Analytics Engine SQL API 需要一个单独的 token,因为 OAuth 登录并不携带 analytics 作用域:
echo "<token>" | npx wrangler secret put ANALYTICS_TOKENtoken 需在 Cloudflare Dashboard 中创建,权限为Account → Analytics → Read。除 cron 外,同一个 token 也授权手动触发聚合(用于验证与回填):
curl -s -X POST https://dbxio.com/api/plugins/archive -H "x-archive-token: <token>"该请求对应 Worker 中x-archive-token头与ANALYTICS_TOKEN的比对(不匹配返回 401),成功后返回 JSON 形式的聚合窗口摘要{from, to, activeKeys, uniqueDays},并带Cache-Control: no-store防止缓存。
7.3 必需的 Secrets 汇总
Env类型(worker.ts)定义了 Worker 的完整运行环境,其中需要人工配置的是两个 secret:
ANALYTICS_TOKEN:Analytics SQL API 访问 token(通过wrangler secret put写入);STATS_SALT:IP HMAC 的盐值,保证一天级身份不可被外部推算(可同样通过wrangler secret put配置)。
CF_ACCOUNT_ID作为明文 var 提供,供 SQL API 的 URL 拼接使用。
八、读取计数器:SQL REST API 查询
当前没有 HTTP 统计端点(还没有消费方,市场 UI 上线时再加),需要直接通过 Cloudflare 的 SQL REST API 查询 Analytics Engine,token 需具备Account → Analytics → Read权限:
curl -s "https://api.cloudflare.com/client/v4/accounts/<account_id>/analytics_engine/sql" \ -H "Authorization: Bearer <token>" \ --data-urlencode "query=SELECT blob1 AS kind, blob2 AS plugin, blob3 AS version, SUM(_sample_interval) AS events FROM DBX_PLUGIN_STATS WHERE timestamp > NOW() - INTERVAL '7' DAY GROUP BY 1,2,3 ORDER BY events DESC"几个由源码印证的关键点:
- 表名用数据集名:SQL 中写的是
DBX_PLUGIN_STATS(wrangler.json 的dataset),不是绑定名PLUGIN_STATS; - query 是 URL 参数:SQL 语句通过 URL 参数传递而非表单体(表单编码的 body 会被解析为原始 SQL 并以 422 拒绝),这与 fetchWindowCounts 的实现一致;
SUM(_sample_interval)即事件数:当前采样比例为 1:1,因此该聚合值就等于事件计数;- 响应解析的容错设计:
parseSqlRows(worker.ts)同时兼容 ClickHouse 风格的{meta, data}(行可能是对象数组,也可能是需与 meta 逐列 zip 的数组)与纯{result}数组,格式漂移时降级而不是崩溃。
另外需要注意,计数器从 Analytics Engine 迁移之后才开始生效;KV 时代被污染的数字(plugin_stats命名空间)已被弃用,查询时不要混淆两个数据源。
九、设计取舍小结
这套统计管线的核心权衡值得借鉴:
- 计数与转发解耦:下载统计只读不改,字节、头、Range 语义原样透传,零性能影响;
- 存储分层:Analytics Engine 承接高频写入(近乎免费),KV 只承接每天每活跃键约 1 次写入的聚合结果,绕开 KV 免费配额瓶颈;
- 身份最小化:客户端随机 uuid 或一天级 IP HMAC,无 PII、无用户指纹,兼具去重能力与隐私下限;
- 窗口幂等语义:
meta:last-success标记 + 写后推进,失败自动重试同窗口;总量累加、天级独立数覆盖,语义清晰且对装饰性统计足够稳健; - 向后兼容:
kind、clientId可选字段让旧版客户端信标自动归入inst,历史基数不断裂。
这套实现为 dbx 的插件市场提供了从"下载 → 安装 → 更新"全链路的量化能力,也为后续市场 UI 上线时的展示层预留了清晰的 KV 读取结构(summary单读 JSON 与uniqd天级独立数)。
- 数据库客户端
- 数据库
- 桌面应用
- CLI
- 后端
- MCP 服务
- AI 应用
【免费下载链接】dbx
25 MB lightweight cross-platform database client for 90+ databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. Built-in AI, MCP Server, CLI, desktop and Docker. | 轻量级跨平台数据库管理工具,支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、达梦等 90+ 数据库,提供桌面端、Docker、CLI、内置 AI 助手和 MCP Server。
相关推荐
typescript-eslint插件市场:插件下载量统计和分析
typescript eslint插件市场:插件下载量统计和分析 概述 typescript eslint作为TypeScript生态系统中最重要的静态代码分析
开发工具静态分析Lint代码质量5分钟快速上手:GuoFeng3古风AI绘画模型完全指南
5分钟快速上手:GuoFeng3古风AI绘画模型完全指南 想要轻松创作出令人惊艳的中国古风艺术作品吗?GuoFeng3古风AI绘画模型正是你需要的工具!这个基于
网络通信CLIBeekeeper Studio 插件发布全流程指南:从 GitHub Release 到插件市场
Beekeeper Studio 插件发布全流程指南:从 GitHub Release 到插件市场 Beekeeper Studio 的插件系统允许开发者以 H
桌面应用数据库客户端开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考