跳至内容
liuzhen932 的小窝
返回

为什么我又从 Twikoo 换回了 Artalk

0x00 又换了一次评论系统

本站的评论区又换了。

从 Artalk 换到 Twikoo,再从 Twikoo 换回 Artalk。评论系统在博客底部安安静静待着,读者可能没什么感觉,对我这个维护者来说却折腾了好几轮。

这次回 Artalk,起因很简单。我受够 MongoDB 了。

当然,事情还得从头说。2026 年 4 月,我放弃了 Artalk,主要担心它的上游维护状态。2024 年 Artalk 发布了 v2.9.1,之后一直没有新的正式版本。仓库里每年都还有提交,可用户真正能拿到的稳定版本长期停在那里,Issues 和 Pull Requests 也越堆越多。

那时的 Twikoo 看起来更有希望。项目持续更新,社区教程多,遇到问题也比较容易搜到答案。于是我把评论数据导过去,重新接入了 Twikoo。

这个选择在当时完全说得通。后来让我失望的重点,落在了它的数据库上。

0x01 我为什么开始讨厌 Twikoo 的 MongoDB

试用了三个月,我发现我开始讨厌 Twikoo 的 MongoDB。

为几条评论单独养一套数据库

Twikoo 的部署文档提供了多种方式。可以使用 LokiJS,也可以通过 MONGODB_URI 连接 MongoDB。实际部署时,我选择了 MongoDB。那时候觉得数据库独立出来更稳,后面才发现自己给博客评论区找了一个长期室友。

我的博客评论量不大。评论、回复、用户和页面,加起来远远没有到需要 MongoDB 出场的程度。Artalk 用 SQLite 就能解决,普通的 PostgreSQL 也足够。到了 Twikoo 这里,我却需要额外考虑 MongoDB 的运行、连接、备份和恢复。

评论服务本身已经够小了,数据库反倒变成了需要单独照顾的部分。它多占一些资源,多一组配置,多一份日志,也多一个以后升级时要确认的组件。

我每次看到 MongoDB 连接字符串,都觉得这件事开始变得过于隆重。一个博客评论框,至于吗?

数据能保存,搬家却变麻烦

熟悉我的人都知道,我很在意自托管服务的数据能不能带走。

Artalk 默认使用 SQLite。停掉容器,复制数据目录,数据库就跟着走了。想看数据,可以用 SQLite 工具直接打开。想备份,复制文件或用数据库工具导出都行。它朴素,至少没有太多惊喜。

MongoDB 当然也支持备份。mongodumpmongorestore 都能用,MongoDB Atlas 也提供了导出方式。问题在于,我得记住 MongoDB 的工具、版本、连接参数和恢复流程。拿到一份 BSON 文件时,我还得先想办法把它还原成适合检查和迁移的内容

如果使用 Atlas,事情还会再多一些。账号、网络访问列表、连接字符串、免费额度和平台限制,都得记着。MongoDB Atlas 对大型项目可能很方便,放在我的博客评论区里却显得特别笨重。

我想保存的是评论数据,结果还得顺便维护一套 MongoDB 的生活起居。****,越想越烦。

MongoDB 没有给我的评论区带来对应的收益

MongoDB 适合很多场景,我也没打算写一篇「MongoDB 一无是处」的文章。它能存 Twikoo 的数据,运行起来也能满足评论需求。

可「能用」与「适合我」差得很远。

评论天然有关系。评论属于页面,回复指向父评论,用户和评论互相关联,站点下面还有页面。我的需求很明确,没有复杂的文档结构,也没有高并发和大规模扩展。MongoDB 提供的灵活性,我基本用不上,它带来的维护工作却全部落到了我头上。

这就是我最失望的地方。Twikoo 解决了评论展示,却让我对评论数据的管理越来越不舒服。

初创公司要的是「一周上线 MVP」,没人关心半年后的数据腐烂。由于缺乏 Schema 约束,随着时间推移,同一集合中的文档结构可能变得五花八门。所以我认为 MongoDB 应该和 MySQL 一样丢进垃圾堆。

同时在当前大环境下最不好受的一点:

MongoDB 为了追求性能,会尽可能地占用内存作为缓存。你一个 JSON 文件夹伪装成的数据库吃我这么多内存是干啥?你觉得内存很便宜?

0x02 Twikoo 一直更新,但这件事救不了我的想法

我得承认,Twikoo 持续更新确实是我当初选择它的重要原因。

相比 Artalk 长时间没有新正式版本,Twikoo 的开发状态更让人安心。版本在发,文档有人维护,遇到问题也能看到项目继续处理。这些优点都是真的。

但项目更新积极,无法改变底层存储方式给我带来的麻烦。每次升级,我还得关注 Node.js 依赖、服务端变化和 MongoDB 数据。更新本身偶尔带来配置调整,这些只能算小问题。真正让我想离开的,始终是 MongoDB。

所以我没有因为 Twikoo 更新太快而离开它。恰恰相反,我很感谢它在那段时间持续维护。只是用了一阵之后,我发现自己更在意评论数据的简单、可见和可搬运。

0x03 Artalk 上游也有自己的问题

回到 Artalk,并不代表上游的问题消失了。

Artalk 在 2024 年 9 月发布 v2.9.1 后,长期没有新的正式版本。仓库每年仍然有提交,开发也没有完全停止,可对普通使用者来说,稳定版本停在旧版本依然会带来不安。上游还有不少积压的 Issues 和 PR,很多问题等不到一个明确的版本节点。

截至本文写作时,上游仓库有 79 个未关闭 Issue 和 27 个未合并 PR。这个数字会变化,文章发布后也可能很快过时,但它能说明当时的状态。

我当初因此离开 Artalk,很合理。现在又回来,也得正视这件事。

我最后做的决定是自己维护一个 fork。与其等一个不确定的正式版本,不如把我关心的修复、构建和镜像放到自己能控制的地方。维护成本确实会落到我身上,至少这份成本是透明的。

0x04 我的 Artalk fork 改了什么

代码放在 cnb/pwsh/Artalk。它仍然是 Artalk,配置方式和大部分用法都能沿用上游。Fork 的重点放在维护和发布,另外合并了一批实际会影响运行稳定性的修复。

发布链路放到了 CNB

我的版本把检查、夜间构建、正式发布、npm 包和 Docker 镜像放到了 CNB。镜像地址是:

docker.cnb.cool/pw.sh.cn/artalk:latest

这样做的好处很具体。代码提交后可以跑 Go 测试、前端构建和代码检查。需要发布时,我可以自己生成镜像,不用等上游发版。

这也意味着我要自己承担维护责任。镜像能不能正常启动,前端资源是否匹配,数据迁移有没有问题,都需要我自己验证。自由和麻烦通常一起出现。

我合并的修复

目前 fork 里比较值得提的改动有这些:

这些改动没有给评论框增加什么花哨功能。它们解决的是另一类问题:后台少出一点莫名其妙的错,数据迁移少留一点垃圾,服务运行久一点也别自己制造竞争条件。

Fork 也远未达到「所有问题都解决」的程度,使用前建议看看 提交记录发布页面

0x05 从 Twikoo 搬回评论

Twikoo 的历史评论可以通过 Artalk 的迁移工具导入。我的流程大致如下:

  1. 从 Twikoo 管理面板导出评论 JSON。
  2. 打开 Artransfer,选择 Twikoo 格式并上传 JSON。
  3. 下载转换后的 .artrans 文件。
  4. 部署 Artalk,创建管理员。
  5. 在 Artalk 控制中心的迁移页面导入 .artrans
  6. 抽查评论数量、回复关系、页面路径、昵称和时间。
  7. 确认数据没问题后,再切换博客前端。

迁移时要重点看 pageKey。Twikoo 里保存的页面地址可能是完整 URL,Artalk 前端却可能只使用路径。两边的格式对不上,评论就会跑到另一篇页面下面,或者看起来像消失了。

我建议先在临时实例导入一遍。原始 JSON、.artrans 文件和数据库备份都留着,确认新实例正常后再停旧服务。

0x06 用 Docker Compose 部署我的版本

下面的方案使用 SQLite 保存数据,OpenResty 负责 HTTPS。需要一台安装 Docker 的服务器、一个已经解析的域名。

先创建目录:

mkdir -p /opt/artalk/data
cd /opt/artalk

创建 compose.yml

services:
  artalk:
    container_name: artalk
    image: docker.cnb.cool/pw.sh.cn/artalk:latest # 或者 nightly
    restart: unless-stopped
    ports:
      - "127.0.0.1:23366:23366"
    volumes:
      - ./data:/data
    environment:
      TZ: Asia/Shanghai
      ATK_LOCALE: zh-CN
      ATK_SITE_DEFAULT: 你的站点名称
      ATK_SITE_URL: https://blog.example.com

ATK_SITE_DEFAULT 要和前端初始化时的 site 保持一致。ATK_SITE_URL 填博客地址,别填评论服务地址。 端口只绑定到 127.0.0.1,公网访问交给 OpenResty。

启动容器:

docker compose up -d
docker compose logs -f artalk

创建管理员(一定要先启动):

docker compose exec artalk artalk admin

按照提示填写管理员信息。之后给评论服务配置一个域名,例如 artalk.example.com

OpenResty 反向代理

server {
  listen 80;
  listen [::]:80;
  server_name artalk.example.com;

  location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

检查并加载配置:

openresty -t
openresty -s reload

HTTPS 证书按自己的方式申请。正式接入前,先确认 https://artalk.example.com 能打开,浏览器也没有证书和混合内容错误。

如果前面还有 CDN,不要无条件信任访客传来的 X-Forwarded-For。源站应该只接受来自可信反向代理的真实 IP 请求头。

接入评论区

Artalk 镜像里包含匹配的前端资源,可以从评论服务域名加载:

<link
  href="https://artalk.example.com/dist/Artalk.css"
  rel="stylesheet"
/>
<script src="https://artalk.example.com/dist/Artalk.js"></script>

<div id="Comments"></div>
<script>
  Artalk.init({
    el: "#Comments",
    server: "https://artalk.example.com",
    site: "你的站点名称",
    pageKey: location.pathname,
    pageTitle: document.title,
    darkMode: "auto",
  });
</script>

前端资源和后端来自同一个镜像,版本更容易保持一致。使用 Astro、Swup 或其他页面切换功能时,切换页面前要销毁旧的 Artalk 实例,否则评论区可能重复挂载,或者继续显示上一篇文章的评论。

0x07 备份和升级

Compose 里挂载的 ./data 就是最重要的数据目录。升级前先备份:

cd /opt/artalk
tar -czf "artalk-data-$(date +%Y%m%d-%H%M%S).tar.gz" data

拉取新镜像并重建:

docker compose pull
docker compose up -d
docker compose logs --tail=100 artalk

除了数据目录,我还建议定期从控制中心导出 Artrans。数据库备份适合完整恢复,Artrans 适合迁移和抽查,两种文件各留一份,心里踏实很多。

0x08 写在最后

我当初从 Artalk 换到 Twikoo,是因为担心 Artalk 上游不再认真维护。Twikoo 的持续更新确实吸引了我,这个判断也没有错。

后来我发现,项目更新积极只是选择评论系统时的一项指标。对我自己的博客来说,MongoDB 才是每天都在提醒我「这套东西有点重」的存在。

为了一个小型评论区维护 MongoDB,备份要管,恢复要管,连接要管,搬家还要重新想一遍。它能工作,可我每次碰到它都不开心。

Artalk 上游依然有版本和维护方面的缺口,所以我选择自己维护 fork。现在评论数据落在 SQLite 里,镜像由我自己构建,数据还能通过 Artrans 导出。以后如果又遇到问题,我至少知道该去哪里改。

这次换回来,核心理由就一句话:Twikoo 的更新让我放心过,MongoDB 却让我一直不爽。


分享这篇文章:

上一篇
别再用封禁 DevTools 来掩盖你的无能
下一篇
liuzhen932 提供的公益服务

人机验证:请刷新页面以加载评论区