Relsola

无需框架简单部署一个博客

在搭建这个博客之前,我已经尝试过很多博客框架,但无一列外,最后都是觉得折腾框架比写内容有趣得多,然后直到最后对框架的折腾丧失热情就此搁置了写博客的想法。

随着我工作和学习的积累,以及 AI 时代的来临,我强烈的感觉到自己必须输出一些东西用来保持我对学习的热情和对抗 AI 的焦虑。

不过,这次我没有选择任何框架,我打算使用 C 语言从零写一个博客引擎,一是为了满足自定义到极致,二是我已经厌倦了框架的臃肿和配置地狱。

随着我接触编程时间越来越长,和经历过几次的博客搭建,我意识到自己需要什么,顺便也想了解下自己的能力界限

技术选型

其实,对于我来说,我熟练掌握的语言全都是写这个博客框架的舒适区(C/C++,JS,Java 等),不过由于工作中一直使用 Java 和 JS,而且我不想上框架,且想追求性能和内存低占用,我最终选择了 C/C++。

不过不使用框架但不代表不使用库,一个人精力和擅长的领域是有限的,所以对于我不熟悉的网络领域,我使用了 mongoose,启动一个 web 服务后这让我几乎再也没有任何阻碍了,但我还使用了 md4c Markdown 解析库,虽然我写过编译器,也能自己实现一个 md 到 html 的解析器,但工作量可能比这个博客引擎还要大得多,显然并不划算,而且 md4c 已经足够快也足够好了,接下来就是使用 C/C++ 实现所有剩下的工作了。

C 和 C++ 的反复切换

技术选型与架构设计

为了让博客足够“简单”,我在设计之初就定下了几个核心原则:

内存管理 —— Arena 分配器

为了避免动态内存分配带来的开销和碎片,我实现了一个基于 mmap 的 Arena(竞技场)内存池。程序启动时一次性向操作系统申请 1GB 的虚拟地址空间(利用 MADV_HUGEPAGE 启用大页,减少 TLB 缺失),然后所有临时对象(如文件内容、解析中间结果)都从这个池子中按顺序分配。一个请求的生命周期结束后,只需重置游标指针即可释放全部内存,几乎零成本。

缓存构建与热加载

服务启动时,会递归扫描 posts/ 和 html/ 目录:

所有 .md 文件被解析,提取 [--] 配置块(标题、日期、草稿标记),并将正文通过 md4c 库转为 HTML。

所有 .html 文件作为模板读取,其中 {{ content }}、{{ title }}、{{ last_update }} 占位符会被动态替换。

生成的内容(首页、每篇文章的完整页面、404 页面)全部存入另一个持久 Arena,并通过哈希映射(FNV-1a 哈希 + 开链法)与 URL 路径关联。

此后每当收到请求,只需在哈希表中 O(1) 查找即可返回静态字符串,完全不涉及文件 I/O。如果文章或模板有更新,重启程序即可重新构建缓存,无需额外工具。

网络服务 —— Mongoose

我选用了轻量级的 Mongoose 库作为 HTTP 服务器。它仅依赖几个源文件,支持事件驱动,能很好地融入我的单线程主循环。主循环中调用 mg_mgr_poll,配合信号处理(SIGINT/SIGTERM)实现优雅退出。同时,我关闭了 Mongoose 自身的日志,转而接入自己的日志系统,以保持输出风格统一。

异步日志 —— 双缓冲 + 条件变量

日志系统是服务可观测性的基石。为了避免写磁盘阻塞请求线程,我设计了双缓冲区异步队列:

两个缓冲区轮流充当“活动”和“冲刷”角色。

日志格式化在请求线程完成(使用线程局部缓存的时间字符串,减少 localtime 调用),然后将日志拷贝到活动缓冲区。

当缓冲区超过阈值(或每 1 秒超时),后台工作线程被唤醒,交换缓冲区,将数据刷入文件。

日志文件支持轮转(最多 3 个,每个 3MB),保证磁盘空间不会无限膨胀。

Markdown 解析 —— md4c

我采用 md4c 库作为 Markdown 解析器,它支持 GFM 扩展(表格、任务列表、下划线)。解析结果通过回调函数直接追加到 ChunkCollector 链表中,最终合并成一个连续的 HTML 字符串,整个过程中无需额外拷贝,内存利用高效。

路由与响应

请求到达时,解析 URI,从缓存哈希映射中获取对应的 HTML 字符串。若不存在则返回缓存的 404 页面。所有响应均附带正确的 Content-Length 和 Content-Type,符合 HTTP/1.1 规范。响应本身也是直接从内存中发送,零拷贝(mg_send 直接传递指针)。

未来的探索

当前版本已经能满足日常写作和访问的需求,但距离“完美”还有很长一段路。我计划在以下几个方面继续演进:

这个博客项目虽小,却像一面镜子,映照出我对简洁、高效和可控的执着。它不仅仅是一个静态页面的容器,更是我不断学习和实验的乐土。如果你也对“手搓”基础设施感兴趣,欢迎一同交流——毕竟,最好的学习方式就是亲手造一个轮子。