我试图使用 CGI 和 ERB 创建我的网站,但是当我在网上搜索时,我看到有人说我应该始终避免CGI
使用Rack
.
我知道 CGI 会 fork 很多 Ruby 进程,但如果我使用FastCGI
,只会创建一个持久化进程,并且它也被 PHP 网站采用。PlusFastCGI
接口一次请求只创建一个对象,性能非常好,而 Rack 一次创建 7 个对象。
我不应该使用 CGI 有什么具体原因吗?或者这只是错误的假设,使用 CGI/FastCGI 完全可以?
CGI,我的意思是接口以及围绕它的通用编程库和实践,是在不同的时间编写的。它将请求处理程序视为通过环境变量和标准 I/O 流连接到网络服务器的不同进程。
这在当时是最先进的,当时还没有我们今天所想的真正的“Web 框架”和“嵌入式服务器模块”。因此...
同样,CGI 模型为每个连接生成一个新进程。虽然如今生成进程本身很便宜,但繁重的 Web 应用程序初始化——读取和解析大量模块、建立数据库连接等——使这变得相当昂贵。
同样,CGI 模型明确提到环境变量和标准输入作为请求和处理程序之间的接口。但谁在乎?这比应用程序设计师通常应该考虑的要低得多。如果您查看基于 CGI 的库和代码,您会发现其中大部分都鼓励“业务逻辑”以及表单解析和 HTML 生成,这现在被广泛视为一种危险的混合问题。
与 Rack::Builder 之类的东西相比,编码人员立即考虑将命名空间映射到操作,这对更广泛的 Web 应用程序意味着什么。(突然间,我们可以自由地争论语义网和 REST 的优点等等,因为我们没有考虑根据用户提供的输入生成单选按钮。)
是的,可以在 CGI 之上实现像 Rack::Builder 这样的东西,但这就是重点。它必须是建立在 CGI 之上的抽象层。
尽管 CGI 在其限制范围内工作得非常好,尽管它很简单且被广泛理解,但 CGI 经常被不可控地抛弃。如果您只知道 CGI,您也可能会被解雇。
不要使用 CGI。请。这不值得。回到 1990 年代,当时没有人知道这似乎是个好主意,但那时脚本并不常见,用于处理表单提交等特殊情况,而不是驱动整个网站。
FastCGI 是一种“更好的 CGI”的尝试,但它在很多方面仍然存在不足,特别是因为您必须管理您的 FastCGI 工作进程。
Rack 是一个更好的系统,而且效果很好。如果您使用 Rack,您有多种托管系统可供选择,甚至是非常简单可靠的Passenger 。
我不知道你说 Rack 一次创建“7 个对象”是什么意思,除非你的意思是有 7 个不同的 Rack 进程以某种方式运行,或者你在实现中犯了错误。
我想不出一个 CGI 会比 Rack 等价物更好的实例。
关于 CGI、Rack 等到底是什么存在很多混淆。正如我在这里所描述的,Rack 是一个 API,FastCGI 是一个协议。CGI 也是一种协议,但在狭义上也是一种实现,而且你所说的与 FastCGI 完全不同。所以让我们从背景开始。
早在 90 年代初期,Web 服务器只是从磁盘上读取文件(HTML、图像等)并将它们发送到客户端。人们开始希望在请求时进行一些处理,而出现的早期解决方案是运行一个程序,将产生的结果发送回客户端,而不仅仅是读取文件。用于此的“协议”是为 Web 服务器提供一个 URL,该 URL 被配置为作为程序执行(例如,/cgi-bin/my-script
),然后 Web 服务器将在其中设置一组环境变量,其中包含有关请求的各种信息和使用标准输入上的请求正文运行程序。这被称为“通用网关接口”。
鉴于这为每个请求都派生了一个新流程,它显然效率低下,而且您几乎肯定不想在大容量网站上使用这种动态请求处理方式。(启动一个全新的过程在计算资源方面相对昂贵。)
一种提高效率的解决方案是,而不是启动一个新进程,而是将请求信息发送到一个已经在运行的现有进程。这就是 FastCGI 的全部意义所在;它维护了一个与 CGI 非常相似的接口(您有一组包含大部分请求信息的变量,以及请求正文的数据流)。但是,它不是设置实际的 Unix 环境变量并使用主体 on 启动一个新进程stdin
,而是向已经在机器上运行的 FCGI 服务器发送一个类似于 HTTP 请求的请求,在该服务器上指定这些变量的值和请求主体内容。
如果 Web 服务器可以以某种方式将程序代码嵌入其中,这将变得更加高效,因为它只运行代码本身。您可以如何执行此操作的两个经典示例是:
将 PHP 嵌入 Apache,以便“Apache 服务器代码”只调用属于同一进程的“PHP 服务器代码”;和
根本不运行 Apache,而是让 Web 服务器用 Ruby(或 Python,或其他)编写,并加载和运行更多为处理请求而定制的 Ruby 代码。
那么 Rack 是从哪里来的呢?Rack 是一种API,它允许处理 Web 请求的代码以通用方式接收它,而不管 Web 服务器如何。因此,给定一些 Ruby 代码来处理使用 Rack API 的请求,Web 服务器可能会:
因此,无论您使用的是 CGI、FastCGI、其他进程间协议还是进程内协议,都没有区别;您可以使用 Rack 执行任何操作,只要服务器知道它或正在与可以理解 CGI、FastCGI 或其他任何内容并根据该请求调用符合 Rack 的代码的进程交谈。
所以:
对于性能扩展,您绝对不想使用 CGI;您想使用 FastCGI、类似的协议(例如 Tomcat 协议),或直接在进程内调用代码。
如果您使用 Rack API,则无需在早期阶段担心您在 Web 服务器和程序之间使用哪种协议,因为像 Rack 这样的 API 的全部意义在于您可以在以后更改它。