LeanCloud 遗憾谢幕:基于 EdgeOne Blob 打造高性能 PV/UV 访客统计
本文最后更新于 2026年5月31日 下午
2026 年 1 月 12 日,LeanCloud 官宣停服。对于数以万计的静态博客主而言,这无异于一场“地震”:原本依赖它实现的文章阅读量(PV)与站点访客(UV)统计将在一年后彻底停摆。
面对终局,博主们面临抉择:是继续寻找下一个易损的 BaaS 平台,还是斥资租赁云服务器、投入繁琐的运维成本去“大炮打蚊子”?
其实,对于追求轻量的静态博客,我们需要的仅仅是一个纯粹、快速且自主的计数器。相比复杂的服务器自建方案,基于边缘计算的 SaaS 化改造显然更契合 Hexo 的极简灵魂。
![]()
与其被动等待下一个“关停通知”,不如利用 Serverless + Blob 存储 技术,打造一个轻量、完全可控的计数器(甚至还是免费的)。
这就是 OpenKounter 诞生的故事。
需要先说明一点:OpenKounter 并不是一开始就“奔着 Blob 去”的。项目早期就是基于 EdgeOne KV 做计数存储的,同时也使用过 EdgeOne Pages 的 Node Functions 来承载管理后台和复杂 API。后来 EdgeOne Pages 的函数体系升级,原来的 Node Functions 被统一纳入 Cloud Functions 概念,并进一步支持 Go、Python 等运行时;而 OpenKounter 的主存储,也在这个演进过程中从 KV 迁移到了 EdgeOne Pages Blob。
换句话说,OpenKounter 的技术演进更准确地说是:KV 存储 → Blob 存储,Node Functions → Cloud Functions(Node.js 运行时)。前者是项目存储层的主动迁移,后者更多是 EdgeOne Pages 平台能力和命名体系的升级。
危机时刻
让我们回到 2026 年 1 月发布停服公告的那一刻。
当你像往常一样打开 LeanCloud 控制台查看数据时,映入眼帘的是一行醒目且冰冷的公告:
LeanCloud 官方公告(2026-01-12):平台将于 2027-01-12 正式停止对外服务。

虽然此刻你的博客阅读量还在正常显示,但倒计时的钟声已经敲响。你知道,如果不采取行动,一年后的今天,所有文章辛辛苦苦积累的阅读数据,都将随着服务器的关闭而彻底归零。

你的第一反应是:“我的数据怎么办?”
幸运的是,LeanCloud 提供了数据导出功能。但导出之后呢?这些 JSON 文件该往哪儿放?重新找一个第三方服务?还是自己搭建一个后端?
如果选择自建,传统方案需要:
- 一台云服务器(价格不高,主要是需要维护)
- 一个数据库(MySQL/MongoDB)
- 一套后端框架(Express/Flask/Django)
- 定期维护、备份、监控……
对于一个月度 PV 大概 1W 次的网站来说博客来说,单独维护这一套内容,有一些太“重”了。尤其是 Hexo 这样的静态博客网站:

有没有一种方案,既免费、又轻量、还能完全掌控数据?
答案是:Serverless + 边缘计算 + Blob 存储。尤其是 Hexo 博客也是部署在静态托管服务上的,完全可以利用同一平台(EdgeOne Pages)的 Pages Functions 和 Blob 存储来实现计数器功能。
EdgeOne Pages
在介绍 OpenKounter 之前,我们先聊聊它的“地基”——EdgeOne Pages。
EdgeOne Pages 是腾讯云推出的 Serverless 静态网站托管服务,类似于 Cloudflare Pages、Vercel 或 Netlify。它集成了静态托管、Edge Functions、Cloud Functions、KV、Blob 等能力。它的核心能力包括:
- 静态资源托管:自动部署你的前端项目(Vue、React、Hexo 等)。
- Edge Functions:在边缘节点运行的轻量级 Serverless 函数,适合低延迟、无 npm 依赖的简单逻辑;早期 OpenKounter 的 KV 读写就依赖它完成。
- KV 存储:面向 Edge Functions 的 Key-Value 持久化存储,特点是就近访问、最终一致,适合轻量配置和简单状态;这也是 OpenKounter 早期版本的主存储。
- Cloud Functions:平台升级后的统一函数形态,目录位于
cloud-functions/,支持 Node.js / Go / Python 运行时;原先常说的 Node Functions 可以理解为 Cloud Functions 的 Node.js 运行时。 - Blob 存储:面向 Pages Functions 的分布式对象存储,支持
@edgeone/pages-blobSDK、JSON 读写、目录列举、预签名上传 URL,并且可以在需要时使用强一致读取;这是 OpenKounter 当前版本的主存储。 - Skills 能力:EdgeOne Pages 官方提供 Dev / Deploy Skills,让 AI Agent 可以按照平台规范完成函数开发、环境检查和自动化部署。

从 KV 到 Blob
早期 OpenKounter 使用 EdgeOne KV 保存计数器。这个选择在当时很自然:KV 和 Edge Functions 绑定紧密,写一个轻量计数 API 很方便;而且对于个人博客来说,KV 的免费额度和访问延迟都非常友好。
不过,随着管理后台、Passkey、OIDC、导入导出、旧数据迁移等功能逐渐增加,项目对“写入后马上读到最新状态”的要求也变高了。EdgeOne KV 的定位是边缘 Key-Value 存储,数据在全球多节点间按最终一致模型同步;它仍然适合很多轻量场景,但对 OpenKounter 这种要维护计数器文档、白名单、Token、Passkey Challenge 的应用来说,Blob 的强一致读取更合适。
EdgeOne Pages Blob 是面向 Pages Functions 的分布式对象存储。官方文档里常见的例子是图片、文档、用户上传文件等非结构化数据,但它也很适合存放按目录组织的 JSON 数据集。
对 OpenKounter 来说,Blob 的关键价值不在于“像 Redis 一样快”,而在于:它既有对象存储的持久化能力,又能在读路径上切换强一致模式。这非常适合计数器、配置、登录状态这类“刚写完就希望立刻读到最新值”的业务。
| 能力 | 传统数据库 / Redis | EdgeOne Pages Blob |
|---|---|---|
| 典型用途 | 关系查询、缓存、Session、限流计数器 | JSON 数据集、用户文件、配置、计数器状态 |
| 使用方式 | 需要自建/租用实例,维护连接和权限 | 在 Pages Functions 中通过 @edgeone/pages-blob 直接调用 |
| 数据模型 | 表 / 集合 / 丰富数据结构 | 对象 Key + 内容,内容可为文本、JSON、ArrayBuffer、Blob、Stream |
| 一致性 | 取决于具体数据库和部署方式 | 默认最终一致;可对 Store 或单次读取启用 strong 强一致 |
| 目录与列举 | SQL 查询或 Scan | store.list({ prefix }) 按 Key 前缀列举 |
| 部署成本 | 需要实例、账号、网络和备份维护 | 随 EdgeOne Pages 项目使用,零运维 |
举个例子:用 Redis 做页面计数,你需要:
1 | |
用 EdgeOne Pages Blob,可以在 Cloud Function 中直接读写 JSON:
1 | |
Blob 存储也不是万能的。它不是 Redis,没有内置 INCR、事务和复杂查询;强一致读取也会比默认边缘缓存读取略慢。但对于 OpenKounter 这种轻量计数器来说,它的强一致读取、JSON 持久化和目录化对象管理,反而比早期 KV 方案更贴合需求。
sequenceDiagram
participant U as 👤 用户
participant EO as ☁️ EdgeOne
participant CF as ⚙️ Cloud Functions
participant Blob as 📦 Blob 存储
Note over U,EO: 🏢 传统方案
U->>EO: 访问博客页面
EO->>EO: 云服务器处理
EO->>EO: Express/Flask 应用
EO->>EO: 数据库查询
EO-->>U: 返回页面 (200-500ms)
Note over U,Blob: ☁️ EdgeOne Pages 方案
U->>EO: 访问博客页面
EO->>EO: CDN / Pages 静态托管
alt 静态资源
EO->>EO: CDN 缓存命中
EO-->>U: 返回静态资源 (< 50ms)
else 计数 API
EO->>CF: Cloud Functions 执行
CF->>Blob: 强一致读取 / 写入 JSON
Blob-->>CF: 返回最新计数
CF-->>U: 返回计数结果
end
Note right of EO: 零运维 + 自动扩展
EdgeOne Pages 的免费额度非常慷慨:
- 安全加速流量 / 请求:不限量
- Edge Functions 请求:300 万次/月
- Cloud Functions 请求:100 万次/月
- Blob 存储空间:免费版套餐下单账户存储容量 1 GB
对于个人开发者来说,这些额度足够用到天荒地老。当然,如果你对 SLA 有较高的要求,那么更推荐使用自有服务。
OpenKounter
有了 EdgeOne Pages 这个”地基”,我们就可以开始搭建 OpenKounter 了。首先是项目地址:
- Github 地址: https://github.com/Mintimate/open-kounter
- CNB 地址: https://cnb.cool/Mintimate/tool-forge/open-kounter

OpenKounter 的设计哲学是:简单至上,性能优先。
我没有使用关系型数据库(MySQL、PostgreSQL 等),而是基于 EdgeOne Pages Cloud Functions + Blob 存储 来实现计数逻辑:
graph TB
User[👤 访客] -->|访问博客| Blog[Hexo 博客页面]
Blog -->|加载| Adapter[🔌 adapter.js]
subgraph EdgeOne[☁️ 腾讯云 EdgeOne Pages]
Adapter -->|/api/counter| Function[⚙️ Cloud Functions]
Admin[🔑 管理员] -->|访问| Dashboard[📊 Vue 管理后台]
Dashboard -->|管理 API| Function
Function -->|@edgeone/pages-blob| Blob[(📦 Blob Store)]
Legacy[🧭 旧版 Edge Functions] -->|读写旧 KV| OldKV[(旧版 KV)]
Function -->|迁移接口兼容| Legacy
end
style EdgeOne fill:#e1f5ff,stroke:#01579b,stroke-width:2px
style Function fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style Blob fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
style OldKV fill:#eeeeee,stroke:#999999,stroke-width:1px
简单说(技术视角):
- 零运维 / 低成本 — 完全 Serverless,EdgeOne 的额度通常足够个人开发者使用。
- 从 KV 平滑演进到 Blob — 早期版本用 Edge Functions + KV 完成轻量计数;当前版本通过
@edgeone/pages-blob创建默认名为open-kounter的 Blob Store,并在核心读取路径使用consistency: 'strong',避免旧版 KV 在多节点同步时可能出现的短暂不一致。 - 从 Node Functions 演进到 Cloud Functions — 项目原本就使用过 Node Functions 承载复杂 API;随着 EdgeOne Pages 升级,现在统一放在
cloud-functions/下运行,并继续使用 Node.js 运行时。 - 无缝替换 LeanCloud — 提供兼容的
adapter.js作为客户端接入 Demo,Hexo / Fluid 等静态博客可以按原来的计数器元素接入。 - 数据可控 & 易备份 — 数据存在你的 EdgeOne 账号;管理员界面支持导入/导出 JSON,也支持从 LeanCloud 或旧版 KV 数据迁移。
- 认证方式更完整 — 除 Token 外,还支持 Passkey 无密码登录和 OIDC 单点登录。
![]()
实际效果
纸上得来终觉浅,让我们看看 OpenKounter 在实际博客中的表现。在我的 Hexo 博客中,OpenKounter 的 API 响应时间稳定在 50ms ~ 100ms 之间:

这个响应时间包含了:
- Cloud Functions 的执行时间
- Blob 存储强一致读取与写入的耗时
- 网络传输时间
与传统方案相比:
| 方案 | 典型响应时间 | 说明 |
|---|---|---|
| 自建服务器 + 数据库 | 200-500ms | 需要建立连接、执行 SQL |
| LeanCloud | 100-300ms | 云端服务,受网络波动影响 |
| OpenKounter (EdgeOne) | 50-100ms | Serverless API + Blob 强一致读写,零运维 |
Blob 默认可以走边缘缓存实现快速读取;而 OpenKounter 的计数、配置、认证状态更看重“写完立刻读到最新值”,因此核心路径使用强一致模式。相比旧版 KV,这个选择牺牲了一点极限读取速度,但换来了更稳定的数据一致性体验。
架构设计
OpenKounter 代码量不大,核心也就几百行。但无论是早期 KV,还是现在的 Blob,它们都不是传统数据库:没有 SQL、没有事务、没有 INCR,复杂查询和并发更新都需要自己设计。
咱们平时用惯了 MySQL 或 MongoDB,换到这类 Serverless 存储可能会有点不适应。
早期 KV 的优势是轻:一个 Key 对应一个计数器,Edge Functions 里读写很直接;后来功能变多后,Blob 的优势开始更明显:它适合把计数器、系统配置和认证状态组织成 JSON 文档,并且可以在读路径上启用强一致。
![]()
为了解决这些痛点,我用了几种设计模式来搭”骨架”.
Blob 文档模式
早期 OpenKounter 使用 counter:{页面路径} 这样的 KV 键值对保存计数器。切换到 Blob 后,当前版本将计数器集中保存到一个 JSON 文档里:
- Store:默认
open-kounter,也可以通过环境变量OPEN_KOUNTER_BLOB_STORE或BLOB_STORE_NAME覆盖。 - 计数器文档:
system/counters.json - 系统状态文档:
system/state.json
system/counters.json 的结构大致如下:
1 | |
当用户访问 /posts/hello-world/ 时,系统会读取这个计数器文档,并通过 items[target] 找到对应记录:
1 | |
这种设计看起来不像单 Key KV 那么“教科书式 O(1)”,但对于个人博客规模来说更实用:管理后台列表、导出、导入、排序都可以在一个文档内完成,避免维护额外索引。
Blob 锁与强一致模式
Blob 没有 Redis 的原子 INCR,所以 OpenKounter 在更新计数器文档时使用了一个轻量锁:
1 | |
这里利用了 Blob SDK 的 onlyIfNew 条件写入能力:只有锁对象不存在时才写入成功。如果锁已经存在,当前请求会短暂等待并重试;如果锁过期,则主动删除后重新竞争。
计数器批量自增时,逻辑大致是:
1 | |
同时,OpenKounter 在创建 Store 和读取 JSON 时都启用了强一致读取:
1 | |
这也是新版从 KV 切换到 Blob 的核心原因:旧版 KV 在全球同步上偏最终一致,写入后跨节点读取可能出现短暂延迟;而计数器、白名单、Token、Passkey 等状态更适合强一致读路径。
Passkey 认证
OpenKounter 内置了基于 WebAuthn 的 Passkey 认证,支持指纹、Face ID 等生物识别方式登录管理后台,告别繁琐的 Token 密码输入。
核心流程分为注册和认证两个阶段:
sequenceDiagram
participant U as 👤 用户
participant F as 🖥️ 前端
participant C as ⚙️ Cloud Function
participant Blob as 📦 Blob Store
Note over U,Blob: 注册流程
U->>F: 点击绑定 Passkey
F->>C: generateRegistrationOptions
C->>Blob: 保存 Challenge (expiresAt 5min)
C-->>F: 返回注册选项
F->>U: 调用 WebAuthn API
U-->>F: 返回凭证
F->>C: verifyRegistration
C->>Blob: 验证并删除 Challenge
C->>Blob: 保存凭证
C-->>F: 注册成功
Note over U,Blob: 认证流程(管理令牌)
U->>F: 验证 Passkey 更新 Token
F->>C: generateAuthenticationOptions
C->>Blob: 保存 Challenge
C-->>F: 返回认证选项
F->>U: 调用 WebAuthn API
U-->>F: 返回签名
F->>C: generateManagementToken
C->>Blob: 验证并删除 Challenge
C->>Blob: 保存管理令牌 (expiresAt 5min)
C-->>F: 返回管理令牌
F->>C: 使用令牌更新 Token
Blob 存储结构设计如下:
| Key | 说明 | 过期策略 |
|---|---|---|
passkey/users/{userId}.json |
用户信息(token、凭证 ID 列表) | 永久 |
passkey/credentials/{credentialId}.json |
凭证信息(公钥、计数器、设备信息) | 永久 |
passkey/challenges/{challengeId}.json |
临时 Challenge | 写入 expiresAt,访问时校验并删除 |
passkey/management-tokens/{tokenId}.json |
临时管理令牌 | 写入 expiresAt,访问时校验并删除 |
locks/passkey/users/{userId}.json |
用户对象更新锁 | 短 TTL,异常时可过期回收 |
后端在处理注册和认证时,会先清理用户的旧 Challenge,防止脏数据堆积:
1 | |
Blob 本身不会像 Redis 那样帮你执行业务级 EXPIRE 逻辑。OpenKounter 对 Challenge、管理令牌这类临时数据采用的是应用层过期策略:
- 写入时保存
createdAt和expiresAt。 - 读取时检查
expiresAt,过期则立即删除并返回无效。 - 创建新的 Challenge 前主动删除旧 Challenge,避免用户对象指向脏数据。
因此,expiresAt 是安全网,主动清理才是主要策略。
OIDC 单点登录
除了 Token 和 Passkey,OpenKounter 当前也支持 OIDC(OpenID Connect)单点登录。如果你已经有 Authelia、Authentik、Keycloak、Zitadel、Casdoor 等身份提供商,就可以把 OpenKounter 接入现有账号体系,而不是单独记一个后台 Token。
OIDC 在 OpenKounter 里是一个渐进增强能力:Token 登录始终可用,OIDC 需要先配置环境变量,并由管理员在后台完成身份绑定后才会出现在登录页。这样可以避免刚部署时因为 OIDC 配错导致自己进不了后台。
整体流程如下:
sequenceDiagram
participant A as 🔑 管理员
participant F as 🖥️ OpenKounter 前端
participant C as ⚙️ Cloud Function
participant I as 🪪 OIDC Provider
participant B as 📦 Blob Store
Note over A,B: 绑定流程
A->>F: 使用 Token 登录后台
A->>F: 点击绑定 OIDC 身份
F->>C: /api/oidc/login?mode=bind&token=...
C->>B: 写入 state / nonce / bind token
C-->>I: 重定向到授权端点
I-->>C: 回调 /api/oidc/callback?code&state
C->>I: 用 code 换取 token / userinfo
C->>B: 校验管理员 Token,保存 oidc.sub/email/name
C-->>F: oidc_bound=true
Note over A,B: 登录流程
A->>F: 点击使用 OIDC 登录
F->>C: /api/oidc/login?mode=login
C->>B: 写入一次性 state / nonce
C-->>I: 重定向到授权端点
I-->>C: 回调 code / state
C->>B: 校验绑定身份 sub 是否一致
C->>B: 写入一次性 OIDC Session
C-->>F: oidc_session=...
F->>C: /api/auth action=oidc_verify
C->>B: 消费 Session,返回实际管理 Token
需要配置的环境变量包括:
| 变量名 | 说明 |
|---|---|
OIDC_ISSUER |
OIDC Provider 的 Issuer URL,例如 https://auth.example.com/realms/master |
OIDC_CLIENT_ID |
OIDC 客户端 ID |
OIDC_CLIENT_SECRET |
OIDC 客户端密钥 |
OIDC_REDIRECT_URI |
回调地址,例如 https://你的域名/api/oidc/callback |
在身份提供商侧,需要把 Redirect URI 配成 OpenKounter 的回调地址,也就是:
1 | |
OpenKounter 会通过 OIDC_ISSUER/.well-known/openid-configuration 自动发现授权端点和换码端点。绑定成功后,OIDC 身份会保存到 Blob 的 system/state.json 中;登录过程中临时产生的 state 和 session 则分别保存在 oidc/states/*.json 与 oidc/sessions/*.json,并通过 expiresAt 做应用层过期控制。
OIDC_CLIENT_SECRET 属于敏感信息,只应配置在 EdgeOne Pages 环境变量中,不要写进前端代码或提交到 Git 仓库。
此外,OIDC 登录页入口只有在“环境变量配置完整 + 后台已绑定身份”后才会展示;如果入口没有出现,优先检查这两项。
Hexo 接入示例
为了让 OpenKounter 能够无缝替代 LeanCloud,我们提供了一个 Hexo 客户端接入示例 adapter.js。
这个脚本是专门为 Hexo 博客(特别是 Fluid 主题)设计的客户端工具包,它演示了如何在静态博客中集成 OpenKounter 的计数功能。主要特性包括:
- 读取配置:从 Hexo 配置文件中读取
server_url等参数 - 智能收集:在页面加载时,自动检测页面中的计数器元素(PV、UV、页面浏览数)
- 批量请求:将多个计数器的更新请求打包成一个批量请求
- 实时显示:更新页面上的计数显示,提供即时反馈
- 本地环境过滤:支持
ignore_local配置,避免本地开发时污染数据 - UV 去重:使用 localStorage 实现 24 小时内的 UV 去重逻辑
1 | |
这个 Demo 展示了如何将 OpenKounter 集成到 Hexo 博客中。对于其他静态网站生成器(如 Hugo、Jekyll 等),你可以参考这个实现,根据自己的需求进行调整。
这样,Hexo 主题(如 Fluid)无需修改核心代码,只需引入 adapter.js 并配置 server_url 即可完成从 LeanCloud 到 OpenKounter 的迁移。
支持创作
制作教程不易,如果热心的小伙伴,想支持创作,可以加入我们的电圈(还可以解锁远程协助、好友位😃):
- Mintimate的电圈: https://ifdian.net/a/mintimate
- Mintimate的微信赞赏码 👉 如果认为本教程对你很有帮助,可以请我喝咖啡 ☕
志同道合的小伙伴也是知音难觅。
- 开发者爱好群: 👉 如果你对云服务器、CDN、云数据库和Linux等云计算感兴趣,亦或者喜欢编程、设计、产品、运营等领域,欢迎加入我们的开发者爱好群,一起交流学习(目前可能就我一个人?🤔,毕竟才刚刚创建~)。
当然,也欢迎在B站、YouTube或微信公众号上关注我们:
- Bilibili: https://space.bilibili.com/355567627
- YouTube: https://www.youtube.com/@mintimate/featured
- 微信公众号: MintimateBlog
更多:
部署指南
部署 OpenKounter 非常简单,你只需要一个腾讯云账号(用于 EdgeOne Pages 部署),并且开通 Pages 的功能:

部署项目
EdgeOne Pages 支持直接从 GitHub 仓库部署,并且支持浏览器端的无代码部署流程。点击下方的按钮,直接将项目部署到你的 EdgeOne Pages:

部署完成后,需要确认项目已开启 Blob 能力。
配置 Blob
OpenKounter 当前版本使用 Blob 作为主存储。与旧版 KV 不同,Blob Store 的命名空间由 SDK 创建:首次请求触发 getStore({ name: 'open-kounter' }) 后,平台会自动为当前项目创建名为 open-kounter 的 Blob Store。
你需要做的事情很少:
- 进入 EdgeOne Pages 控制台,找到你的项目。
- 进入项目设置里的 存储 / Blob 存储,确认项目已开通 Blob 能力。
- 访问一次项目页面或
/api/init//api/counter等接口,让函数触发 Blob Store 创建。 - 如果你希望自定义 Store 名称,可以设置环境变量
OPEN_KOUNTER_BLOB_STORE,默认值为open-kounter。 - 如需预置或找回管理员 Token,可以设置环境变量
ADMIN_TOKEN。 - 调整 Blob 或环境变量配置后,建议重新部署项目以使配置稳定生效。
OpenKounter 使用的主要环境变量如下:
| 变量名 | 必需 | 说明 |
|---|---|---|
OPEN_KOUNTER_BLOB_STORE |
否 | 自定义 Blob Store 名称,默认 open-kounter |
ADMIN_TOKEN |
否 | 预设管理员 Token,优先级高于 Blob 中保存的 Token |
PASSKEY_RP_ID |
否 | Passkey RP ID,默认使用当前域名 |
PASSKEY_RP_NAME |
否 | Passkey 显示名称,默认 Open Kounter |
OIDC_ISSUER |
否 | OIDC Provider 的 Issuer URL |
OIDC_CLIENT_ID |
否 | OIDC 客户端 ID |
OIDC_CLIENT_SECRET |
否 | OIDC 客户端密钥 |
OIDC_REDIRECT_URI |
否 | OIDC 回调地址,例如 https://你的域名/api/oidc/callback |
如果你是从旧版 OpenKounter KV 版本迁移,才需要额外绑定旧 KV 命名空间,变量名仍为 OPEN_KOUNTER。新部署用户不需要创建或绑定 KV。
登录后台后,可以在“数据备份”页面使用“旧 KV 迁移到 Blob”功能,将旧 KV 中的计数器、白名单和认证配置导入 Blob。
也可以使用 Pages Skills
如果你使用支持 Skills 的 AI 编程工具,也可以安装 EdgeOne Pages 官方 Skills,让 Agent 自动完成开发和部署流程。
- Dev Skill:适合新增 API、Cloud Functions、Edge Functions、中间件等功能。它的决策树会区分:轻量低延迟逻辑用 Edge Functions;需要 KV 持久化时使用 Edge Functions + KV;需要 npm 包、数据库、WebSocket 或复杂后端时使用 Cloud Functions 的 Node.js 运行时;如果需要高性能编译型接口或 Python 生态,也可以选择 Go / Python Cloud Functions。
- Deploy Skill:适合把项目部署到 EdgeOne Pages,会自动完成环境检查、CLI 安装、登录认证、项目构建和部署上传。
这也解释了 OpenKounter 的演进:早期 KV 读写离不开 Edge Functions;管理后台和复杂 API 则更适合 Node Functions。平台升级后,这类 Node.js 后端能力统一归入 Cloud Functions,并扩展出了 Go、Python 等运行时。
官方安装方式可以直接在 AI 对话中输入:
1 | |
安装后,你可以直接说“部署到 EdgeOne Pages”,Agent 会按照 EdgeOne Pages 的规范完成后续步骤。
测试使用
假设,你的 EdgeOne Pages 域名是 https://your-domain.edgeone.pages.dev,你可以通过以下 API 来测试计数功能:
1 | |
成功响应将返回 code: 0 和计数数据;管理后台首次访问会引导你设置管理员 Token(也可在部署时通过环境变量 ADMIN_TOKEN 预置和找回密码)。
接入博客
在你的 Hexo 博客中(以 Fluid 主题为例),adapter.js 本质上是一个客户端接入的 Demo,你可以直接使用它,也可以根据自己的需求进行修改。
你可以将仓库中的 client/adapter.js 下载下来,放到博客的 source/js 目录下(例如重命名为 openkounter.js),然后在主题配置中引入:
1 | |
即使你的主题(如 Fluid)已经内置了 OpenKounter 支持,你也可以通过这种方式引入自定义脚本来覆盖默认行为(需先禁用主题自带的 OpenKounter 或确保脚本执行顺序)。
同时,在博客配置文件中添加 OpenKounter 的服务地址配置(adapter.js 依赖此配置):
1 | |
当然,同时还需要修改主题的其他配置文件,通常是 analytics.ejs、statistics.ejs 等文件。修改完成后,重新生成并部署博客,刷新页面,你就能看到阅读量统计恢复正常了!
如果你是 Fluid 主题用户,可以查看我的这个 Fork 的这次 Commit: Mintimate/hexo-theme-fluid/commit/dace585020440402641db7e87a189245aa83a0a3,它展示了如何修改主题来兼容 OpenKounter。

如果你使用的是其他主题,可能需要修改 adapter.js 中读取配置(CONFIG 对象)和获取 DOM 用于显示的逻辑。
管理后台
为了管理方便,我在 OpenKounter 里内置了一个基于 Vue 3 的可视化管理后台。

访问你的 EdgeOne Pages 域名,首次访问会引导你设置管理员 Token。之后,你可以:
- 查看计数器列表:按更新时间排序,支持分页。
- 手动修改计数值:比如从 LeanCloud 迁移数据时,可以批量导入。
- 配置域名白名单:防止恶意刷量。
- 导出/导入数据:一键备份所有计数器数据。
- Passkey 无密码登录:支持生物识别(指纹、Face ID)登录,告别繁琐的 Token 密码输入。
管理后台的设计非常简洁,所有操作都在一个页面内完成,无需跳转。
数据迁移
如果你之前使用 LeanCloud,可以通过以下步骤迁移数据:
- 在 LeanCloud 控制台导出数据(JSON 格式)。
- 登录 OpenKounter 管理后台。 进入”数据备份”页面,点击”导入数据”。
- 上传 JSON 文件,系统会自动解析并导入。

导入完成后,所有计数器的值都会恢复到迁移前的状态。
常见问题(FAQ)
Q: OpenKounter 能直接替代我原来的 LeanCloud 计数吗?
A: 能 — 对于阅读数/站点 PV/UV 等“计数”功能,adapter.js 已经兼容常见主题的调用方式;但它不是完整的 LeanCloud SDK,复杂的 AVObject/查询逻辑需手动替换或迁移。✅
Q: 怎样防止被刷量?
A: 使用域名白名单(保存在 Blob 的 system/state.json 中)+ 后端的 checkOriginAllowed 校验;对于管理接口请使用 Token、Passkey 或 OIDC。🔒
Q: 免费额度够用吗?
A: 对于个人博客,大多数情况下 EdgeOne Pages 的免费额度(Cloud Functions 请求、静态资源流量和 1 GB Blob 存储等)足够;高流量网站请考虑付费方案或缓存优化。💡
Q: 如何在本地调试管理后台?
A: 进入项目目录并运行:
1 | |
具体的调试,可以参考官方文档:
Q: 如何导出备份?
A: 管理后台提供“导出”功能,或使用 API action: "export_all"(需管理员 Token),会返回 counters 和 allowedDomains 的完整 JSON。📦
END
OpenKounter 不仅是一个工具,更是一种思路的展示:利用 Serverless 和边缘计算,我们可以用极低的成本构建高可用、高性能的应用。
LeanCloud 的关停,看似是一场危机,实则是一次机遇——它让我们重新思考:我们真的需要一个”大而全”的 BaaS 平台吗?
对于个人博客来说,答案是否定的。我们需要的只是一个简单、可靠、可控的计数器服务。而 OpenKounter,正是这样一个存在。
如果你正在为 LeanCloud 的替代方案发愁,不妨试试 OpenKounter。它开源、免费、且完全属于你。
欢迎 Star 和 Fork,如果有任何问题,欢迎在 Issue 中反馈!
最后,如果你觉得本篇教程对你有帮助,欢迎加入我们的开发者交流群: 812198734 ,一起交流学习,共同进步。

