No fun allowed
。如果这还不够,请编辑它以隐藏这个 CSS 类: xeblog-slides-essential
。这样做可能会使演示页面更难理解。我的博客设计过度,以至于人们认为它是一个静态站点
演讲的 YouTube 视频
YouTube 链接(如果 iframe 不适合您,请告诉我)
成绩单
速度。安全。开发经验。无所畏惧的并发。这些都是你与用 Rust 编写的程序相关联的东西。再来点流行语怎么样?优雅之类的词?这是一个很好的。
我是 Xe Iaso,我将分享关于我的博客如何运作的血腥细节,以及为什么人们经常将其误认为是一个静态网站。系好安全带,放松一下,我们今天要了解互联网。
我是谢亚索。您可能已经在那个橙色网站或其他橙色网站上看到过我的博客。我也学习哲学,一直在写小说。我在 Tailscale 工作,担任基础设施大法师,我负责开发人员关系,我的博客在某种程度上是学习 Nix 和 NixOS 的最佳资源之一。
本次演讲将包含有关网站设计等的意见。这些意见是我自己的,不是我雇主的意见。
网站是社会结构。只有服务器会使用这种奇怪的 HTTP 协议,如果幸运的话,有时还会吐出一种称为 HTML 的标记语言。然后这个 HTML 被非常有原则的人或“网络浏览器”理解,然后它全部被转换成作者或设计者想要的大致样子。
我在 emacs 中用 markdown 写博客的所有帖子。有时我很少在我的 MacBook、iPad 甚至手机上的 Apple Notes 中进行大脑倾倒或初步起草,但最终都会进入 emacs 进行发布。
这个降价在 YAML 中有一些重要的内容。这有元数据,如标签、它所在的“系列”、与帖子相关的流式录制以及计划公开发布的时间。这些都是模板使用的,以确保我不会忘记将其放入文章中。
但是,主要关注的是帖子的内容。我打出来的字。在此过程中,我在名为comrak的降价解析器之上有机地发展了我自己的自定义降价方言。 Comrak 由朋友制作,是本网站最重要的部分。
然而,多年来我发现香草降价并不足以满足我的需求。我在我的博客上增加了一些功能,这些功能需要更多花哨的东西,比如对话片段和新添加的 AI 生成的“英雄图像”。
起初我只是实现了一个 hacky markdown 扩展。它将对话片段逻辑应用于与带有奇怪 URL 方案的降价链接匹配的任何内容。不幸的是,最终无法很好地扩展,因为对话片段变得更加复杂,比如当我需要添加链接时。
所以我引入了一个名为lol_html
的库。我使用它使用一组模板将我的自定义 HTML 元素转换为一堆其他 HTML。这让我可以偶尔用 HTML 来编写 Markdown 无法表达的东西,然后我可以依靠我的博客引擎将这些短代码翻译成人们在网站上看到的内容。
在我的博客中,我使用了一个名为ructe的模板引擎,它在 HTML 之上采用一种奇怪的元语法,然后吐出 Rust 代码。
这意味着当您加载像我的主页这样的页面时,您会遇到一个将该主页呈现到字符串缓冲区的函数。该字符串缓冲区是我的网站将其扔回空白处的内容,希望它最终会全部归还给您。作为做这一切的副作用,它发生得很快。真的很快。如此之快以至于它比静态网站还要快。事实证明,用 ram 提供东西非常快!
当我说快速时,我的意思是我已经非常努力地寻找一些可以击败我的网站的静态文件服务器。我真的很努力。我将我的站点与 Nginx、openresty、tengine、Apache、Go 的标准库、Rust 中的 Warp、Rust 中的 Axum 以及最后一个将站点数据编译到 ram 中的 Go 标准库 HTTP 服务器进行了比较。
它们都不是更快,保存预编译的 Go 二进制文件(大约 200 MB,不适合我的需要)。这很有趣。我不小心创造了一些如此高效的东西,以至于很难真正表达它有多快。
…但是 ructe 的语法很糟糕。我必须在模板本身中指定我的代码类型。我必须确保自动生成的模板代码正在导入我需要的任何非默认特征。它有效,但是ehhhh它有点糟糕。
<!-- templates/definition.rs.html --> @(term: String, definition: String) < dt >@term</ dt > < dd >- @definitions</ dt >
所以我一直在和莫德一起玩。 Maud 是一个过程宏库,可让您在编译时将其自己的领域特定语言转换为 HTML。你可以让你的组件成为正常的 Rust 函数。我将 Maud 用于我所有的“简码”,并且我一直在慢慢地转换我的网站以使用它。这非常棒。强烈建议您检查一下。
use maud::{html, Markup};
fn definition ( term : String, definition : String) -> Markup { html!{ dt {(term)} dd { " - " (definition) } } }
您看到我使用这些的最重要的事情之一是我在博客文章中的小“对话片段”。这最初是为了绝对扣篮那些对有人将毛茸茸的艺术放在信息安全博客文章中感到愤怒的同性恋者,但也让我尝试一种更苏格拉底式的对话风格来帮助更详细地解释事情。我现在用这种风格写所有东西,不得不回去编辑它以供工作博客使用。我的同事可以证实这一点。
这种灵活性还让我可以添加诸如用 AI 生成的“英雄图像”之类的东西。我使用这些来帮助使帖子在视觉上更有趣。我仍在用所有这些来改进我的风格并试图让事情变得更好,但我在 CSS 方面绝对糟糕。
我最喜欢这个网站如何运作的部分之一可能会让人群中的理论计算机科学家开始哭泣。当我的博客将所有内容从磁盘加载到内存中时,它会将所有帖子存储在一个链表的道德等价物中。当您作为读者查看我的一篇文章时,它会对我的每一篇文章进行 O(n) 查找以确定要显示的文章。
通常这会很可怕,尤其是在我的博客获得大量流量的情况下(如这里这个方便的图表所示)。您会认为在最坏的情况下对每个帖子进行查找以查找最大数据集上最常见的内容会使性能变得非常缓慢主动损害。
然而,这是我打出我的陷阱卡的时候!当您查看分析时,您会发现最常阅读的文章是最近发布的文章!这意味着在大多数情况下它实际上并不是 n 查找的大 O。它是恒定的时间复杂度。从理论上讲,这种设计是您在接受工作机会后通常会发现的那种可怕的东西,但实际上它很好。不过这有点奇怪,我将来可能需要重新考虑这一点,但这已经扩大到近 300 个帖子,所以我认为现在还可以。
当我的网站启动时,它会将磁盘中的每个帖子读入内存。 Rust 让这变得非常容易。使用 tokio,我可以安排一堆工作,然后等待它们全部完成。这让我可以将负载分散到每个 CPU 内核,这样帖子的加载速度可以达到迭代完成时的 12 倍。加载完成后,它会对它们进行排序并将它们放入博客数据结构的列表中。
我可以在一行 Rust 中做到这一点,它就像是 50 行 Go。 Rust 让我的认知复杂度更低,因为我可以只依赖需要照顾的事情,而不必一直重新发明轮子。我认为在高级逻辑中,让编译器处理使计算机工作的细节。这很棒。
这意味着我的网站实际上是无状态的!这允许我将它移动到我想要的任何服务器,以防发生非常糟糕的事情。
我也可以在 ram 中获取所有这些数据,然后将其转换为我想要的任何提要。我目前支持 RSS、Atom 和 JSONFeed,因此您可以使用您通常使用的任何阅读器订阅我的博客。
JSONFeed 允许自定义扩展,我已经使用了一个,它在我的前面的问题中为您提供了一些额外的元数据,这些元数据没有在 JSONFeed 结构本身中公开。通常这并没有显示出任何有用的东西。我在这里放置与帖子相关的 Twitch 和 YouTube 链接、讨论页中幻灯片的链接或博客文章系列的名称(如果存在)。
我不知道是否有人使用这些,但我已经开始将它们用于我的一些内部管道事情。
这个概念允许我在我的博客上发布内容,然后让其他内容接管在 Twitter 和 Mastodon 上发布这些帖子,并带有这样的消息。一切都是自动化的。我不必抬起一根手指。
除了 Patreon。 Patreon 的 API 不允许您以编程方式生成帖子,有时我会忘记将帖子链接到我的顾客。我正在努力改善这一点,但我真的很想把它交给机器并停止关心它。
我使用它的另一个主要用途是 WebMentions。 WebMentions 类似于 Twitter 上的 at-mentions,但适用于 Internet 上的任何网站。它是另一种 IndieWeb 协议,有数量惊人的网站支持,同时也是 Twitter 和 Mastodon 之类的桥梁。 Mi 接收并存储我获得的所有网络提及。当我的网站启动时,它会联系 mi 并获取它加载到内存中的每个帖子的 webmentions 列表。
这意味着从您发送 WebMention 到它显示在我的博客上可能会有一些延迟,但实际上这没关系。我希望它更快,但这意味着必须将 WebMentions 数据库移动到我的主博客应用程序中,我不知道我是否准备好这样做,因为它会使移动它变得更加复杂。
所以我之前在我的博客上提到过,我现在将所有东西都托管在一台大型 NixOS 服务器上。这意味着我可以在该服务器上相当持久地存储东西,但我还提到我的网站是无状态的,并将其状态转移到有状态的微服务。
您可能想知道“为什么要对自己这样做?”。我有一个很好的理由,但是为了解释为什么我想花一点时间来追溯我的网站托管的历史。
Heroku 的免费层是我用来打入科技领域的东西之一。当我在山景城开始我的工作并获得我以前的域名时,我可能正在使用 Heroku 来托管那个网站(我没有当时的笔记,我感觉很糟糕)。
那时,我的网站展示了我使用称为Lapis的 Web 框架编写东西的能力,您可以将其视为内置在 nginx 一侧的 Rails for Lua。我网站的这个变体已经使用了几年,直到我在 2016 年底重写了它。
该网站工作方式的很大一部分是它在每次页面加载时解析每个帖子的降价。这让我可以非常快速地编辑和测试内容,这使得撰写帖子和实时预览成为可能。在我的第一篇文章出现在 Hacker News 的首页之前,我没有解决这个问题,这意味着我的网站有点慢,但它几乎没有承受负载。
之后,我设置了一个名为OlegDB的缓存服务器。 OlegDB 是一些朋友用 C 编写的键值存储,它是一个关于蛋黄酱走得太远的笑话。我在我的网站中使用 OlegDB 来缓存每个降价帖子的渲染 HTML。当您加载一个页面时,它会向 OlegDB 服务器发出另一个 HTTP 请求,以从缓存中获取内容。这比解析每个页面加载的降价要快,最终使我的网站在 Hacker News 的愤怒中幸存下来。
在我的网站最初部署在 Heroku 上一段时间后,我将其移至运行Dokku的服务器上。 Dokku 是一个可自托管的 Heroku 克隆,可让您在您拥有和操作的服务器上使用 Docker 运行类似 Heroku 的环境。从那以后,我已经使用 Dokku 多年了,很长一段时间以来,当我尝试将任何东西部署到云中时,它是我首先想到的。它有模板可以用来启动你当时能想到的任何数据库,当我想实验时启动基础设施并在我完成后将其关闭是微不足道的。无需额外费用。
那时我对价格非常敏感。与在一个每月 5 美元的 Heroku 应用程序上托管一个应用程序相比,能够在同一台每月 5 美元的服务器上托管多个应用程序是一个巨大的优势。
自Go 社区 slack成立以来,我一直是其成员。一次又一次地,我看到人们想要一个使用 Go 标准库作为其框架的 Web 应用程序示例,但没有真正好的示例。我还达到了一个性能优化点,我不知道如何让我在 Lapis 上的网站运行得更快,所以我有点被书呆子狙击了,决定用 Go 重写我的网站。
第一次迭代使用带有PureScript和 React 前端的 Go 后端。这工作了一段时间,但在我意识到我的目标受众使用不支持 JavaScript 的奇怪浏览器后,有时我完全删除了客户端渲染,并让服务器像“传统网站”一样向客户端吐出 HTML。这让我能够优雅地在 Hacker News 的死亡拥抱中幸存下来,这也是我开始将所有东西都放入 ram 的原因。我网站的 Go 端口像冠军一样处理负载。
这是我开始将所有内容放入一个巨大的链表的时候。它比使用缓存服务器快得多,但唯一的缺点是它使网站启动速度变慢。但这不是一个实际问题。
我承认,我的博客是个特例。我的网站获得的流量比您想象的要多得多(每月超过 100 GB,鉴于我的网站主要包含文本,这确实令人印象深刻)。
当我的文章流行起来时,它们会很快流行起来,然后人们就会开始查看我网站上的其他页面。我有非常独特的性能要求。幻灯片上的数字是我出现在新闻聚合器或其他“病毒式传播”帖子首页的次数。在将近 300 篇文章中,这意味着我的文章在很短的时间内获得大量浏览量的机会不到 1/10,因此我需要确保网站代码尽可能快地运行最常用的最常用的路线。
有一次,我的博客被加载到足以让我的 Dokku 服务器崩溃。来自纯文本 HTML 响应和 RSS 回复。必须付出一些东西,所以在虚弱的时刻,我与魔鬼达成协议。
我把我的博客放在 Kubernetes 上,作为我学习如何将它用于工作的一部分。我是一个非常亲力亲为的人,我需要一个本地副本才能真正感觉我了解如何使用它。所以我决定委托货运列车寄信,并用 Digital Ocean 建立了一个 Kubernetes 集群。
一旦我克服了最初的设置初期问题(你需要多少alpha 组件来服务 web 应用程序???),这效果非常好,并且它工作了很长时间。我能够使用 GitHub Actions 进行持续部署,并且最多可以让我的博客付出最少的努力。我专注于写作和出版被降级为机器。
但是当它炸毁时,它比单服务器炸毁时更糟。我无法访问摔倒的服务器。我在 Kubernetes 集群上有足够多的应用程序,我不能只是通过缩小和放大来解决问题。有时文件系统挂载会卡住,而我没有“重启那个吸盘”按钮来解开它。当这种情况发生时,我的 [个人] Git 服务器将停止工作,当您应该专注于日常工作时,尝试调试非常烦人。过了一会儿,我放弃了。
然后我又被 NixOS 的书呆子狙击了。使用 NixOS,我可以直接指定应该运行什么以及在哪里运行。我拥有的力量超出了普通人仅靠 Docker 和 Kubernetes 所能达到的能力。
我可以塑造有问题的应用程序的世界,然后继续进行,而不是尝试基于过于通用的工具将事物组合成形状。我可以将 nginx 配置为路由到 Unix 套接字,然后我不必关心 Kubernetes 过于通用的图灵完备 YAML 地狱。
我认为这非常棒,但我也是一个 VTuber,所以请用适当大小的盐粒来表达我的意见。
您可以从这一切中获得的最大好处是动态 Web 应用程序可以非常快。特别是如果它们是按目的建造的。如果您在开发时牢记目标,它将很快完成您需要的一切。
我的博客站在巨人的肩膀上。这些人中的每一个人都因为帮助我的博客或这次演讲大放异彩而得到了特别的呐喊。
谢谢!你们真的比你想象的更多。
并感谢您的收看!我将留在聊天中回答我尚未回答的任何问题。如果我错过了您的问题,或者您真的想回答聊天之外的问题,请将其通过电子邮件发送到 xeserv dot us 的 howimadeblog。
我很快就会有这个演讲的书面版本,包括我的幻灯片、演讲的录音以及我今天在博客上所说的一切。
如果您有任何问题,请说出来。我喜欢回答他们,我非常乐意花时间给出详细的答案。
一切安好。