标准电子书是一个项目,它采用公共领域文学的抄本,就像古腾堡项目通常提供的那种,并使用详细的风格手册从中创造出美丽、现代的电子书。来自世界各地的志愿者致力于制作这些电子书,然后我们免费发布它们,不受版权限制。
美国公共领域的书籍通常是至少95年前出版的书籍。(在撰写本文时,这意味着在1927年之前出版。)正如你所想象的,我们花了很多时间在写非常非常古老的书上。那么,为什么不用非常非常古老的技术创建一个网站呢?
在2021,标准电子书服务器提供了大约140万个电子书下载,超过1550万个页面浏览量。这意味着每月的页面浏览量略高于120万次,不包括对我们的OPD和RSS提要的请求,这些请求每月也有数百万次。在过去的几年里,它至少三次登上黑客新闻的头版,都没有出过一身冷汗。我们用一个小小的VPS为一个没有Javascript甚至没有数据库的网站服务!
我们得到的流量并没有那么疯狂。但它展示了一种廉价的、经典的网络技术可以为惊人的流量提供服务,而不依赖我们许多人在启动新项目时本能地触及的任何技术支柱。
SE使用一个带有2GB内存的单核VPS运行其整个操作。它负责:
托管我们所有电子书的所有Git存储库,作为我们制作的电子书的真相来源。(虽然我们也在GitHub上托管我们所有的电子书,但GitHub实际上是我们电子书的一面镜子,而不是来源。我们认为保持本地控制很重要,而自我托管一个类似GitHub的解决方案,允许公众查看电子书来源并提交更正,而不是依赖GitHub,这是我们的长期路线图。)
标准的电子书网站运行在经典的笔记本电脑上。你说那是什么?大腿部没错:LAMP没有M。SE网站背后甚至没有传统的数据库。
我写PHP已经快20年了。这是一种我熟悉的语言和范例。随着年龄的增长,人们对新技术的热度越来越不感兴趣毕竟,当一个人必须钉钉子时,在一个人停止关心和伸手去拿之前,只有那么多不同种类的锤子可以让他感到兴奋。因此,当我想创建一个新网站时,我会使用PHP。
幸运的是,从某种程度上讲,PHP是78%的web用户选择的服务器端语言,因此,如果你选择这种流行的语言,那么至少在未来几十年里,你很容易找到人来维护它。
PHP因其黑客、丑陋的语言而名声扫地。我不会说它是最漂亮的语言,但多年来对它提出的许多其他批评基本上都在现代PHP中得到了解决。
现代PHP还不错,与一些竞争对手相比,它至少有两大优势:
每个请求都是重建世界的完全独立的请求。没有共享状态(除非你愿意)。
它是一种解释性语言,因此在部署时没有需要重新编译的代码,也没有需要重新启动的服务器。你修改了脚本,保存了文件,刷新了浏览器,就这样了。这使得开发和调试非常快速,部署非常容易。
许多人还不知道的是,PHP也是一种相当好的现成模板语言!虽然很容易假设任何PHP web项目至少需要一个大型框架,但事实上,使用基本PHP作为自己的简单模板系统是很容易的。对于许多基本的经典网站,比如SE,这通常足以完成工作;最大的好处是,你不必永远学习和维护一个庞大的第三方框架。
我认为人们真的低估了即使在网站开发完成后维护第三方框架的负担。如果你的网站仅仅存在短短五年,你需要更新多少次框架,哪怕只是为了安全更新,并跟上API更新以确保没有破坏性的更改?对于我再也不想看到的脚手架来说,一次太多了。
请求从Apache开始,通常通过使用mod_rewrite重写URL开始,例如,将电子书路径重写为带有查询字符串的PHP脚本。这在某种程度上相当于服务器端框架中的路由器,只是它是在PHP出现之前完成的。
一旦Apache决定运行哪个PHP脚本,它就会将其转发给PHP-FPM。PHP-FPM是进程管理器,多年前它取代了病态而虚弱的mod_PHP,也是现代PHP网站速度如此之快的主要原因之一。
Apache的性能非常好,当然对我们的网站来说已经足够快了。Nginx最近似乎受到了关注,但Apache是一个可靠的、高性能的工作平台,不应该马上被忽视。再说一次,你知道这是一把锤子。
一旦请求到达PHP,我们就开始使用一个非常非常小的基于文件的模板系统为浏览器创建输出。它使用原始PHP而不是自定义模板语法。正如我前面提到的,PHP本身已经是一种出人意料的优秀模板语言。为什么要重新发明轮子?
在请求的这一点上,我们可能必须先查找一些数据,然后才能将其呈现给浏览器。在这里,人们可以查询数据库,但我们没有数据库。那我们怎么做呢?
您可能还记得,SE服务器还托管了我们所有电子书的所有Git存储库。这些电子书包含丰富的元数据,包括标题、作者、描述、字数、系列信息等。因为我们的电子书库相对较小(在撰写本文时大约630本电子书),所以我们能够将它们全部放在内存中:特别是使用PHP内置的APCu对象缓存。
这个缓存在PHP进程中仍然存在,所以我们只需要在PHP-FPM第一次启动时初始化它一次,并且只需要在电子书更新时更新它。如果PHP进程崩溃或服务器重新启动,网站会在再次启动时从头开始重建缓存。构建这个缓存确实需要几秒钟的时间,但由于很少发生崩溃和重启,这并不是什么大问题。
你可能会认为这是一个非常愚蠢的动态网站的方法。为什么不直接使用数据库呢?
我并不完全反对。但之所以如此,是因为SE一开始是一个小爱好项目,而且在很多方面仍然是一个小爱好项目,小爱好项目希望花时间在肉(电子书)上,而不是土豆(网站)。
要建立一个网站,我们必须解析电子书中的XML,哪怕只是将其插入数据库供以后查找。如果我们已经在用PHP创建电子书对象,为什么不跳过数据库、ORM层、粘合代码、数据模型和所有辅助内容,直接缓存电子书对象本身,以节省开发时间呢?
事实证明,对于我们的小数据集和非常基本的数据处理需求,这种方法是完美的。当然,我们最终会需要一个真正的数据库,但我们可能会首先达到这一点,因为我们希望我们的网站做更复杂的事情,而不是因为我们电子书语料库的大小需要一个。
与此同时,我们仍能为数百万的页面浏览量提供良好的服务。
如果我们的设计决策还没有让你大吃一惊,那么这一次可能会让年轻的开发人员大吃一惊:我们的网站不使用任何Javascript。零(好吧,除了指向manifest.json文件的链接之外,因为没有办法解决这个问题。)
就内容而言,SE网站是一个非常经典的网站。有一个主页,一个搜索页面,上面有一个分页的项目列表,这些项目的详细视图,一些非常基本的表单,以及一些辅助页面,大部分是静态页面。碰巧的是,简单的HTML加上一些巧妙的CSS,就具备了这种网站所需的所有功能:
样式、动画、过渡和响应性功能都是通过简单的CSS处理的。我们对基于JS的UX反模式嗤之以鼻,比如scrolljacking。
表单是普通的HTML,通过无聊的提交按钮发送常规的GET和POST请求。我们不需要花哨的AJAX或加载微调器,当您单击提交时,您的web浏览器可能已经在某处显示了加载微调器。
在短短几年内,服务器端呈现从近乎通用的默认状态变成了一个古怪的遗迹,现在Javascript在一个一切都是API的国度里“显然”是必不可少的,它将由一个巨大的第三方Javascript框架在客户端呈现。但这种新的模式很慢:提供兆字节的Javascript很慢,等待API响应很慢,解析响应客户端和生成页面客户端很慢。
雪上加霜的是,这项工作通常要做两次:一次是在服务器上解析、组装和输出数据,另一次是在客户端解析、组装和呈现数据。
和PHP框架一样,JS框架也是另一种需要无限期维护的第三方依赖关系,即使你的网站不再积极更新。一个曾经流行的JS框架多久会被最新的热点取代?
不仅如此,在源代码处使用精心选择的简单HTML输出可访问的网页要比在客户端JS中拼凑元素时容易得多。
JS框架对于一些更大、非常复杂的应用程序来说是有意义的。但很少有网站是合法的非常复杂的应用程序。通常情况下,它们只是另一个CRUD应用程序——经典web设计和构建的那种基于文档的异步模型。
虽然从JS框架开始开发一个新项目似乎更快,但从您选择的语言的服务器端框架开始开发一个新项目可能也同样快。
我不是反JS的狂热分子。明智地使用Javascript绝对可以增强用户体验。但整个过程是否需要在反应中完成?或者,几行精心放置的jQuery,甚至是vanilla JS可以完成这项工作吗?你的CRUD应用程序需要是通过websockets交付的单页JS应用程序吗?
对于SE来说,这是一个非常经典的web项目,它避开了Javascript,在服务器端呈现,并依赖于经典web的静态文档模型和基本形式进行交互,不仅减少了我们的维护负担,大大简化了开发,而且还加快了用户体验,提高了启动的可访问性。
如今,在AWS或Azure等云服务上创建一个基础网站非常流行。不过,虽然托管我们VPS的公司在技术上是一个云服务,但我们自给自足的愿望在租用机架上拥有裸机之前就结束了,我们不依赖任何其他第三方云服务,如AWS或Azure。
部分原因是因为只要看一眼AWS的首字母缩略词和首字母缩写就让我头晕目眩。
但更严重的原因是,SE作为一种经典的web项目,渴望独立,就像经典web曾经那样。我们目前最大的外部依赖是GitHub,但我们(非常)长期的目标是放弃这一依赖,转而使用自托管Git接口。
从一家云供应商起步会让人失去独立性,因为这往往会导致供应商锁定。例如,一旦你开始使用AWS及其难以理解的eldritch小圈子服务,你就开始需要专业的、专有的知识——我该如何再次设置S3 bucket的权限-要从中挖掘出一个成熟的项目可能非常困难。如果您想更改主机提供商,或存储或备份解决方案,该怎么办?
在我们自己的VPS上运行基本Linux box的最大好处是,所有东西都只是一个通用的、易于理解的平台上的文件。在很大程度上,一台主机上的Ubuntu将与另一台主机上的Ubuntu相同,而Ubuntu是Linux,所以跳到(比如)CentOS并不是一个巨大的飞跃。这些技能具有高度的可转移性,使得将项目转移到其他地方变得更加容易,或者在我们的需求超出其能力时升级服务器。
很多人也选择云服务,因为他们的CDN可以提高请求速度。虽然这当然是真的,但SE的经典网络方法让这一点变得不那么重要。现代网络应用在资产规模上已经变得如此臃肿,并且花了大量时间处理JS,以至于使用CDN从获取静态资产中节省几毫秒的时间对它们来说变得更有价值。但是,由于我们谨慎地控制我们所服务的静态资产的大小和数量,只需将完整的页面发送回客户端,而不是依赖JS来呈现页面,因此突然之间减少毫秒似乎就不那么重要了。如果该网站在远离我们的主机的地理位置上运行速度稍慢,那么,我们在这里提供免费电子书,这不是生死攸关。
云提供商在很多计算任务中都有自己的位置,一旦一个项目开始达到非常大的规模,就开始变得更有意义。但是对于一个基本的网站和伴随而来的基础设施来说,这可能需要惊人的流量——VPS是一种低成本、简单、免锁的方式。非常经典的网页。
对于大多数基础网站来说,除了数据库之外,最大的资源消耗者通常是服务器端语言,它将网页组装起来提供服务。当在不需要任何服务器端处理的文件系统上提供普通文件时,Web服务器非常、非常快。
考虑到这一点,我们使SE服务器提供的大多数文件都是驻留在常规文件系统上的静态文件。我们的OPD和RSS提要是静态文件,只有在书籍更新时才会重新生成;电子书下载是文件夹中的普通文件,其在线阅读版本也是如此;等等
通过执行数据库查找的脚本提供下载服务,或者为每个请求重新生成提要,这是很诱人的。但是,当文件系统中的普通文件性能很好时,这需要做很多工作。将网站保持为一堆静态文件不仅是一种非常快速、非常经典的网站组织方式,而且还为我们带来了。。。
因为PHP不是预编译的,而且我们没有Javascript或CSS可传输或缩小,所以部署网站就像rsync一样简单。
一次rsync调用就可以将我们的开发文件系统推到生产环境中,创建、更新和删除文件,以使两个文件系统匹配,瞧,我们的更改是实时的。
随着语言和框架不断积累越来越深奥的编译过程和包管理器,这种网站只是文件集合的模式变得越来越不寻常。我的NPM版本是最新的吗?我们是否编写了正确的XML脚手架来编译正确的DLL?我们是不是先把打字稿转过来,然后再把它缩小成一个纱线包?鲍尔在吟唱monorepo之前是不是把右边的bulbatron克隆成了Jekyll?谁在乎!我们所要做的就是复制我们的文件系统,它就会正常工作。
SE服务器还负责在电子书发布或更新时构建电子书。这是使用我们的电子书生产命令行工具集完成的。每次更新电子书存储库时,Git钩子都会启动电子书的重建,以便在网站上提供更新版本。
出于几个原因,电子书的制作可能会相当缓慢,而且资源非常丰富,而在我们的低资源VP上,速度甚至会更慢。VPS可以轻松地一次制作一本电子书,同时也可以为网站服务,但一次制作三本电子书可能会让服务器喘不过气来,疯狂地抓着它的胸口。
为了防止这种情况发生,构建使用任务后台处理程序(Task Spooler,简称ts)排队,ts是一个管理Bash脚本队列、按顺序执行脚本并记录其输出的程序。ts是一种小型、简单、Unix-y的方法,可以确保对一系列电子书的频繁更新不会让我们有限的资源同时因一系列构建任务而负担过重,同时避免过度使用比Beanstalk大得多的一体化解决方案。
XHTML是HTML5被忽视的兄弟,也是21世纪中早期传说中的语义网的先驱。它带来了所有单调乏味、令人呻吟的XML包袱,比如名称空间和严重的解析器错误,但只带来了一些小的好处,这些好处主要来自于渲染引擎,而不是其他。
它从未在网络上流行,部分原因是在网站环境中,模板和库生成了太多HTML,以至于很容易在某个地方引入语法错误;与HTML不同的是,在HTML中,语法错误仍然会呈现一些东西,XHTML中最微小的语法错误意味着整个东西都被浏览器抛出,你会看到死亡的黄色屏幕。
尽管XHTML在网络上并不流行,但它仍然存在于一个非常大的空间:电子书。
你看,EPUB,出人意料的优秀(你多久会把一个标准描述为“优秀”?)绝大多数电子书制作者和阅读系统使用的开放式电子书格式基本上是一组捆绑在zip文件中的XML/XHTML文件。你可以解压一本电子书,在你的网络浏览器中打开它的XHTML文件,它看起来就像一个普通的网页。
既然我们的库存是包含XHTML的zip文件,我们想为什么不把网站本身也作为XHTML提供,作为对我们电子书根源的异想天开的致敬?我们做到了。
如果你要开始一个新项目,我不推荐你使用XHTML。这是不可原谅的,几乎没有好处。但对我们来说,这是一种有趣的实验,是项目经理在现实生活中不会让你做的事情——向我们钟爱的EPUB格式致敬,向语义网的宁静梦想致敬。
所有这些都表明,你不需要花太多时间在一台小型服务器上建立一个性能良好、有用的网站,每月可以处理数百万个请求,同时还可以处理其他资源密集型任务。
认为创建“现代”网站需要臃肿的JS框架、复杂的后端框架、大量的软件包管理器、复杂的移动部件(如数据库或专用排队软件)以及专有供应商的超资源、昂贵的云实例,这是一种谬误。经典的网络技术可以让你以每月几分钱的价格为数以百万计的页面浏览量提供服务。您可以免费获得速度、可访问性、可维护性和简单性。
下次当你考虑添加一个JS库来制作一个元素的动画时,问问自己:我能用简单的CSS来实现吗?
下次你要用React创建一个新网站时,问问自己:如果我只是渲染服务器端呢?
下一次,当你在一家专有云提供商上开发一个计算实例时,问问自己:一个5美元的VPS可以吗?
下一次当你想为XHTML5服务时,问问自己:等一下,什么?