觉得是个很有意思的话题~
WSGI 的细节不太了解,但感觉这个东西应该和 CGI 这种设计的模式有关系,应该是同一个时代的东西。
其实可以不用关注 Browser 这一层,关注点放在 HTTP 请求相应的处理流程
类似这样的地址
https://cs50.harvard.edu/college/2021/spring/index.html
访问这个地址,实际干的事情是,遵照 https 协议,和 cs50.harvard.edu
的 443
端口建立 socket,tls 握手连接,然后发送一串类似这样的 HTTP 协议格式的字符:
GET /college/2021/spring/index.html HTTP/1.1
Host: cs50.harvard.edu
User-Agent: curl/7.68.0
Accept: */*
然后服务器拿到了 /college/2021/spring/index.html
这个路径,读取 WebRoot + 这个路径
之下的文件,和部分预定的 Header 拼接,生成 HTTP 格式的响应。
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Cache-control: no-cache="set-cookie"
Content-Security-Policy: frame-ancestors 'self'
Content-Type: text/html
Date: Tue, 08 Jun 2021 17:29:23 GMT
ETag: "62cab4f89abe3a76ee73e69198c91fd9"
Expires: 0
Last-Modified: Wed, 26 May 2021 13:43:23 GMT
Pragma: no-cache
Set-Cookie: AWSELB=C5FB31C71E2F96150D4C17188738B032F5BDA70C6671E1AB68899642D4F05121CA9F21E40D3C87768FD079A49ECB26BB71463DEEA5AAC56080B64C164A165A022AE6FCB51E;PATH=/
Set-Cookie: AWSELBCORS=C5FB31C71E2F96150D4C17188738B032F5BDA70C6671E1AB68899642D4F05121CA9F21E40D3C87768FD079A49ECB26BB71463DEEA5AAC56080B64C164A165A022AE6FCB51E;PATH=/;SECURE;SAMESITE=None
Content-Length: 103217
Connection: keep-alive
<!DOCTYPE html><html lang="en-us"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-sca
...
以上是一个静态文件的请求响应的过程,也是 Web 最早期的工作模式。
后来 Web 继续演化,有了一些动态化的需求,比如说统计访客之类的,希望是可以动态生成访客的数量,而不单纯地输出静态文件。所以这时候就有了动态生成响应的需求,它最早的工作模式就类似这种 cgi 模式。
最古早的 cgi 模式是,把 HTTP 请求的资源路径对应一个 cgi 程序的路径,当客户端请求到这个 cgi 文件路径的时候,把 HTTP 请求的头部转为环境变量,body 对应 stdin,启动这个程序,这个程序执行的输出,就对应到 HTTP 响应的 Header 和 body,然后再返回给客户端。
这方面对应的是 IETF 的 RFC 3875 文档,它定义了 Common Gateway Interface 的标准。
https://datatracker.ietf.org/doc/html/rfc3875
其中最核心的概念在于 Gateway,这里的将 HTTP 请求转换为程序启动的环境变量和 stdin,把 stdout 变成响应这一系列事情就是由 Gateway 去做的。(502 Bad Gateway 的 Gateway 大概也是这个意思,类似返回静态文件的 Gateway / 执行脚本的 Gateway 等等)
同一个时代过来的类似 PHP / Perl / C++ / Java 写的后台,也依赖着类似的概念,本质上应该还是不同的 Gateway 的实现。
WSGI 干的事情,也类似这种,定义了一套接口协议,把 HTTP 请求和 Python 的函数调用对应上。这时候类似 uWSGI 就是一种 Gateway 程序。
PHP 的模型就非常接近古早的 CGI 模式,它用的是 fastcgi 协议(因为古早的 cgi 的 fork-excute 模式的性能实在堪忧),HTTP 请求也会类似 CGI 那样去拆成 $_SERVER 和 $_GET、$_POST 之类的内部表示。一般通常用 php-fpm 来做它的 Gateway(有的是 Apache 的 mod_php)。
Java 的 Servlet 也类似这样的概念,Servlet 容器(Tomcat 之类的)也是充当了这样的 Gateway,把 HTTP 请求转换成了 Java 语言里面的请求对象去处理。
...
可能对于后面发展起来的新语言来说,可能这种 Gateway 的概念被弱化了,所以容易蒙蔽。但我认为本质上应该还是一样的模型,只是最终处理 HTTP 请求的那个 “过程” 在不同语言的特定形态的差异;还有就是语言本身可能内置了一些充当 Gateway 角色的元素。
类似 Node.js 的 HTTP 模块用回调函数来处理 HTTP 请求;Golang 的 net/http 模块给每个请求启动一个新的 goroutine 去处理(不会 golang,但感觉是这个套路);PHP 后面有人做了个异步非阻塞模型的 Swoole 、像 Node.js 那样用回调函数来处理 HTTP,等等等等。
整体可能类似一种 RPC 调用的感觉,这些概念也是语言无关的~
(隐隐约约想起 CSAPP 的讲 IO 的章节好像也讲到了 CGI 的概念)