- 已编辑
不太懂为什么会产生WSGI这个东西?
现在是: Browser -> Nginx -> uvicorn -> WebApp.
直接 Browser -> Nginx -> WebApp不行吗?
是不是将某些网络功能从APP offload到WSGI服务器上了?APP就负责业务逻辑?
有什么比较好的资料、文章讲讲这个东西的必要性\没有会产生什么问题?
不太懂为什么会产生WSGI这个东西?
现在是: Browser -> Nginx -> uvicorn -> WebApp.
直接 Browser -> Nginx -> WebApp不行吗?
是不是将某些网络功能从APP offload到WSGI服务器上了?APP就负责业务逻辑?
有什么比较好的资料、文章讲讲这个东西的必要性\没有会产生什么问题?
觉得是个很有意思的话题~
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 的概念)
突然想到,现在流行的云函数的 API Gateway 也是同样的模型
Using AWS Lambda with Amazon API Gateway - AWS Lambda
只是粒度上的差异
0x0001
gq老哥的回答有很大帮助!其实主要是WSGI的粒度与我的经验有差异。
WSGI的文章老是提到:
WSGI is an interface between web server and web app.
我觉得很奇怪:web server 和 web app不是同一个东西吗?后来看多几篇文章,大概了解这个server是接受请求和返回请求的程序,web app指的是处理具体业务的Python对象。从uvicorn的”奇怪“用法可以看出:
# main.py
from fastapi import FastAPI
app = FastAPI()
通过uvicorn main:app --reload
启动服务器。这里uvicorn(web server)直接找到main.py的app这个全局变量作为web app,不是整个main.py。在我接触过的框架(gin, actix-web)没有这样做法,所以一时间感到很奇怪。
补充一下,无WSGI的视角:
http http
--------> Nginx --------> webserver
WSGI:
http http WSGI\ASGI
--------> Nginx --------> web server ------------> Python Object
WSGI 充当的是一个API网关的作用。Python 本身自己也可以跑 HTTP 服务,解析 HTTP 请求,那为什么还要一个独立的网关来干这个事情呢?
除了前面gq提到的历史原因外,分离的网关也有现实的好处。也就是分担一部分backend的责任,例如解析头、负载均衡、限流等。并同时将相对复杂的 HTTP 请求转换为更容易处理的形式(如 RPC 或 JSON,或 Python Object)。这样,backend代码就只需要在乎业务数据和逻辑就可以了,不需要过分在乎具体的网络协议与传输细节。
网关的作用其实相当于一个带协议翻译功能(HTTP -> RPC)的nginx。
如果是那种功能比较齐全的带负载均衡等功能的网关,或者旨在为多个微服务提供一个统一的一站式接口的网关,比如 Apache APISIX,会倾向于像 nginx 一样运行在单独的进程,甚至独立的机器。
如果只是简单为 backend 提供将 HTTP 请求解析为函数调用的功能,而没有太丰富的其他额外功能(无均衡、无限流等等)的网关,如 gRPC-Gateway,则有时候会将其与 backend 程序集成为一体,作为同一个程序的不同模块运行。(降低网关与 backend 互相调用的开销)
说来也挺神奇的,API 网关这个话题不久前刚刚和 gq 线下聊到过hhh
© 2018-2025 0xFFFF