wsgi和asgi
wsgi和asgi都是Web服务器网关接口。它们是一种规范,描述了Web服务器如何与Web应用程序(客户端)通信,以及如何将Web应用程序链接在一起以处理一个请求。
发展历史
就本质而言,Web开发绝大多数时候是对HTTP协议(当然还有其它协议,例如FTP,SMTP等)的应用,实际上我们能使用socket自制web服务器,但每次都需要自己处理协议的内容,所以你可能会想自己设计个程序来完成对协议的处理。之后每次都使用该程序来完成对协议的处理。基于此出现了CGI(Common Gateway Interface),现在CGI几乎已经看不到了(在嵌入式web领域依旧存在),可以在Apache服务器上进行尝试。
题外话
写到这里,说一些我的个人经历。大学的时候,在飞凌嵌入式开发板(仍然记得是Samsung的S3C6410芯片)上做过boa服务器的移植,嵌入式web开发。现在我在写Python web开发,相当于转行了。之前的经验几乎没什么用。 我想说的是少走点弯路其实是最好的,当然了不走弯路几乎是不可能的。
回到正题,CGI会把HTTP请求Request的Header头设置成进程的环境变量,HTTP请求的Body正文设置成进程的标准输入,进程的标准输出设置为HTTP响应Response,包含Header头和Body正文, 这就是CGI的原理。
一段典型的C语言CGI程序可能如下所示:
代码语言:javascript复制#include <stdio.h>
int main (){
printf("Content-type:text/htmlrnrn");
printf("<html>n");
printf("<head>n");
printf("<title>Hello World - First CGI Program</title>n");
printf("</head>n");
printf("<body>n");
printf("<h2>Hello World! This is my first CGI program</h2>n");
printf("</body>n");
printf("</html>n");
return 0);
}
没想到吧,那个时代的HTML代码是直接被printf出来的。注意,这里标准输出是被重定向了。那时候c,perl还是写web的主力语言。那时候还没有前端工程师。
一个获取GET请求参数的CGI程序可能如下所示:
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *data;
long m,n;
printf("Content-type: text/htmlnn");
printf("<TITLE>Mult Result</TITLE>");
printf("<H3>Mult Result</H3>");
// 服务器把它从http协议接收到的GET请求的数据编码到环境变量QUERY_STRING
data = getenv("QUERY_STRING");
// 数据检查
if(data == NULL) // 如果没有数据
printf("<P>Don't transfer data or transfer error");
else if(sscanf(data,"m=%ld&n=%ld",&m,&n)!=2) // 参数不正确(或者不全)
printf("<P>Error, invalid format, data have to number");
else // 参数正确
printf("<P>%ld and %ld result: %ld", m, n, m * n);
printf("<br><h> OK </h1>");
return 0;
}
如果是POST请求,那么应该是将标准输入进行重定向。
在现在这个时代看来,这种方式实在是过于古老。由于每一次向CGI发送请求,都会生成一个CGI进程,也是导致并发瓶颈的症结。当然了,这个症状并没有限制web开发,因为那个时代根本不会遇到非常高的并发。后来,jsp,asp技术出现了,没有给CGI继续发展的机会。相比CGI,asp(jsp)的性能更加优越,因为它们可以直接在HTML网页中动态嵌入元素而不需要单独引用CGI文件。这就是模板引擎的优势,这个时代渐渐地有了模板工程师,专门负责写MVC模式中的V,而后端工程师们则是专注于写M和C。
关于CGI,更多的内容,感兴趣的读者可以参考下面的文章。
CGI是什么
Python CGI编程
Windows 配置Apache CGI
关于CGI和FastCGI的理解
再后来,有一天,人们又想起来了CGI,然后在此基础上做了修改,形成了FastCGI。FastCGI是Web服务器与处理程序之间通信的一种协议,是CGI的改进版本。由于CGI程序反复加载CGI而造成性能低下,如果CGI程序保持在内存中并接收FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail-Over特性等。
FastCGI这个东西现在也很少见到,几乎只在C 和PHP(PHP改进了它)上能看到它的身影。
WSGI
Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
也就是说wsgi和CGI是同样的东西,都是连接Web服务和Web应用之间的桥梁。只不过两者规范的东西不一样。
wsgi的特殊之处在于简单和通用。
WSGI描述了Server与Framework之间通信的规范,WSGI协议主要包括server和application两部分:
- WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;
- WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。
wsgi的出现是因为web框架会限制用户对可用web服务器的选择,而人们希望设计出一个Web服务器和Web应用程序之前的简单通用接口。这样就将框架的选择与 Web服务器的选择分开,让用户可以自由选择适合他们的配对,同时让框架和服务器开发人员可以专注于他们喜欢的专业领域。
WSGI协议其实是定义了一种server与application解耦的规范,有了这个,写Web框架的人,不需要考虑server部分的编写;写server的人就可以专注于写server,而不是写出来server没人用。这也导致了Python的Web框架泛滥。写框架的人不用管server。但是流行的框架就那么几个(Flask,Django,Tronado)。
更多详细的细节以及背景可以阅读PEP-3333,它是PEP-333的更新版本,略有修改以提高Python 3下的可用性,并纳入了WSGI协议的几个长期事实上的修正案。
现在的Python提供了一个内置的模块wsgiref,该模块是wsgi规范的实现。关于该模块的使用可以参考手册wsgiref
关于wsgi的更多内容,可以阅读下面的文章。
python从小白到入门:10分钟搞懂WSGI协议
江湖儿女——WSGI
Python Web开发最难懂的WSGI协议,到底包含哪些内容?
WSGI
ASGI
ASGI是WSGI的精神继承者,WSGI是用于Web服务器、框架和应用程序之间兼容性的长期存在的Python标准。
WSGI成功地在Python网络空间中提供了更多的自由和创新,而ASGI的目标是将这一点继续推进到异步Python的领域。
原文如下:
代码语言:javascript复制ASGI is a spiritual successor to WSGI, the long-standing Python standard for compatibility between web servers, frameworks, and applications.
WSGI succeeded in allowing much more freedom and innovation in the Python web space, and ASGI’s goal is to continue this onward into the land of asynchronous Python.
这么一说,大家都明白了,ASGI和WSGI是一样的东西。那么为什么需要ASGI?以及ASGI的具体内容,可以参考asgi文档.
ASGI被设计为WSGI的超集,即ASGI是兼容WSGI的。 ASGI定义了两者之间的转换方式,允许WSGI应用程序通过转换包装器(在asgiref库中提供)在ASGI服务器内运行。
最后,我们可以看一眼Django3.2中的wsgi.py和settings.py中的内容。就明白了大致的过程。
代码语言:javascript复制# wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chatroom.settings')
application = get_wsgi_application()
代码语言:javascript复制# settings.py
WSGI_APPLICATION = 'chatroom.wsgi.application'