当前位置 博文首页 > p515659704的博客:一文读懂WSGI和ASGI

    p515659704的博客:一文读懂WSGI和ASGI

    作者:[db:作者] 时间:2021-09-11 16:58

    这一章谈谈用户到我们web应用中间经过的相关协议,具体介绍和pyhton相关的WSGI和ASGI,我先把结论列出来,详细描述请看下面介绍!

    请大家先记住这张图,带着问题和整个框架去看比较易于了解

    CGI,WSGI,ASGI、框架以及Web服务器的关系:比如flask,这是一个同步框架,同时也是一个Web应用,一个请求需要经过Web服务器和WSGI服务器才能和框架“交流”数据。下面请看一张图来澄清整个链路
    在这里插入图片描述
    对这张图有一段话解释这里借鉴一下,参考[1]

    1. 首先nginx 是对外的服务接口,外部浏览器通过url访问nginx,

    2. nginx 接收到浏览器发送过来的http请求,将包进行解析,分析url,如果是静态文件请求就直接访问用户给nginx配置的静态文件目录,直接返回用户请求的静态文件, 如果不是静态文件,而是一个动态的请求,那么nginx就将请求转发给uwsgi,uwsgi 接收到请求之后将包进行处理,处理成wsgi可以接受的格式,并发给wsgi,wsgi 根据请求调用应用程序的某个文件,某个文件的某个函数,最后处理完将返回值再次交给wsgi,wsgi将返回值进行打包,打包成uwsgi能够接收的格式,uwsgi接收wsgi 发送的请求,并转发给nginx,nginx最终将返回值返回给浏览器。

    3. 要知道第一级的nginx并不是必须的,uwsgi完全可以完成整个的和浏览器交互的流程,但是要考虑到某些情况

    ? a. 安全问题,程序不能直接被浏览器访问到,而是通过nginx,nginx只开放某个接口,uwsgi本身是内网接口,这样运维人员在nginx上加上安全性的限制,可以达到保护程序的作用。

    ? b. 负载均衡问题,一个uwsgi很可能不够用,即使开了多个work也是不行,毕竟一台机器的cpu和内存都是有限的,有了nginx做代理,一个nginx可以代理多台uwsgi完成uwsgi的负载均衡。

    ? c. 静态文件问题,用django或是uwsgi这种东西来负责静态文件的处理是很浪费的行为,而且他们本身对文件的处理也不如nginx好,所以整个静态文件的处理都直接由nginx完成,静态文件的访问完全不去经过uwsgi以及其后面的东西。

    通用型网关接口:CGI、FastCGI

    网关接口是一种协议,为了实现加载动态脚本。CGI程序则是实现了CGI协议的一种程序

    Web服务器网关接口协议:WSGI、ASGI、uwsgi

    网关接口是用于Web应用与Web服务器进行通讯。其中WSGI、ASGI是专为python设计的网关接口。uwsgi是uWSGI服务器自有的传输协议

    实现了Web服务器网关接口的软件有:uWSGI(注意大小写)、uvicorn、gunicorn

    同步异步:同步是指执行是串行的,需要处理完当前任务在处理一下;异步指并行的,现有的任务不影响下一个任务的执行。类比到请求上面就是假如同时有两个请求进来,在同步处理的框架下第二个请求需要等第一个结束之后才能响应,而异步就可以并行处理

    Web框架

    • 诸如Flask、Django这类pyhton Web框架,可以被启动当成服务器的原因,都是因为其自带启动了WSGI服务器,才允许Web服务器与Web框架之间进行连接。(效率极其低下,一般生产可以使用gunicorn、gevent等WSGI服务器)

      • 这里请参考flask启动的案例,Flask.run()

      • def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
            """Runs the application on a local development server.
        
            Do not use ``run()`` in a production setting. It is not intended to
            meet security and performance requirements for a production server.
            Instead, see :ref:`deployment` for WSGI server recommendations.
            ****"""
            *	 # 省略了部分代码
            *	 # 省略了部分代码
            *	 # 省略了部分代码
            from werkzeug.serving import run_simple
        
            try:
                run_simple(host, port, self, **options)
            *	 # 省略了部分代码
            *	 # 省略了部分代码
            *	 # 省略了部分代码
            
        
      • 这里注意到run_simple,我们继续跟踪进去看其代码,如下:

      • def run_simple(
            hostname,
            port,
            application,
            use_reloader=False,
            use_debugger=False,
            use_evalex=True,
            extra_files=None,
            reloader_interval=1,
            reloader_type="auto",
            threaded=False,
            processes=1,
            request_handler=None,
            static_files=None,
            passthrough_errors=False,
            ssl_context=None,
        ):
            """Start a WSGI application. Optional features include a reloader,
            multithreading and fork support."""
        
      • 可以看到这里就是做启动WSGI服务的简单准备工作,具体代码就不分析,感兴趣的小伙伴可以去跟踪看下生成的过程

    • 而FastAPI这类高性能、纯Web框架,是没有带WSGI或者ASGI协议的服务器启动功能的,所以我们在启动的时候需要用uvicorn等命令

    总结:框架是为了让我们专注于逻辑代码和业务的实现,我们可以无需将时间和精力花在数据如何传输,如何编码,请求和响应是如何执行的。不过这中间可能会涉及到服务器之间的层层关系,所以我们有必要了解一下整个链路来龙去脉

    协议,规范支持的请求协议(常见,未列全)同步/异步支持的框架
    CGIHTTPCGI程序
    WSGIHTTP同步Flup,Flask
    ASGIHTTP,HTTP2,WebSocket等同步/异步FastAPI,Quart,Sanic,Vibora,Tornado

    CGI

    CGI(Common Gateway Interface),通用网关接口,它是一种协议,是Web服务器和独立进程之间的协议,它会把HTTP请求的Request的Header设置成进程环境变量,HTTP请求的Body正文设置成进程的标准输入,进程的标准输出设置为HTTP响应的Response。

    早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,随着技术的发展,需要用到动态技术。由于Web服务器不能直接运行动态脚本,为了解决Web服务器与应用程序之间数据互通,出现了CGI。由于效率低下,不安全等原因,目前在生产环境基本被抛弃。

    CGI程序

    CGI(Common Gateway Interface)是一个接口规范或协议,实现其规范的程序叫做CGI程序。Web客户端发送HTTP请求到Web服务端,之后服务端通过CGI程序进行处理数据,处理完的结果在逐层返回,请看下图
    在这里插入图片描述
    我们常说的Web服务器一般是用于加载静态文件的请求,有一些动态脚本的请求,就会需要Web服务器创建一个新的进程来启动CGI程序,也就是CGI程序进行加载动态脚本,之后由Web服务器返回给客户端。

    CGI程序可以是任何有标准输入和输出的脚本语言或者编程语言实现的,但由于其漏洞大,安全性低,每次有动态页面请求都会启动一个CGI程序,因此服务器压力特别大。逐渐出现了mod_perl、FastCGI等增强版本

    WSGI

    Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI,它是一种专为python定义的接口规范,用于web服务端和web应用(框架)之间的连接,如此web应用可以一起处理一个请求,同时也是基于CGI进行设计的

    通俗的理解,WSGI也是一种规范协议,主要是建立一个适用于服务器与Web框架之间的接口。那么跟CGI类似的,你可以通过自己的编程语言实现符合WSGI的应用程序,下面看一下WIKI上面的例子

    def app(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/plain')])
        yield "Hello world!\n"
    

    其中

    • 第一行定义了一个名为 app 的 callable服务器网关接口#cite_note-2),接受两个参数,environ 和 start_response,environ 是一个字典包含了 CGI 中的环境变量。
    • 第二行调用了start_response,状态指定为“200 OK”,消息头指定为内容类型是“text/plain”。start_response 也是一个 callable,接受两个必须的参数,status(HTTP 状态)和 response_headers(响应消息的头)。
    • 第三行将响应消息的消息体返回。

    这里的流程请注意:

    1. Web客户端(浏览器)发送请求

    2. HTTP服务器接收请求

    3. 实现了WSGI的服务器接收请求(可以和2合并)

      a. 把环境变量和请求参数以及一个callback回调函数传递给Web应用程序

      b. Web应用程序处理完之后通过回调函数返回给HTTP服务器

    WSGI 协议包含两个部分,一个是 WSGI 服务器端(server),另一个则是应用程序端(application)。即 WSGI 对上文中的 Web 应用程序也有比较严格协议规定。固编写 Web 应用程序通常需要使用支持 WSGI 协议的应用框架,如 Django、Flask 等。

    ASGI

    异步网关接口(Asynchronous Server Gateway Interface,是WSGI的扩展版本,旨在为Python Web服务、框架和应用之间提供一个标准的异步接口。其本身可以提供同步和异步应用,并且可以并行处理。还能处理多种通用协议,包括HTTP,HTTP2和WebSocket。同WSGI一样,需要有独立的服务器实现这种异步的网关接口,比如Daphne、Uvicorn、Hypercorn等,参考[3]

    参考:

    [1]: https://blog.csdn.net/u014761344/article/details/40146597 - nginx uwsgi wsgi django是什么关系

    [2]: https://www.cnblogs.com/wongbingming/p/11002978.html - WSGI详细讲解

    [3]: https://asgi.readthedocs.io/en/latest/implementations.html - ASGI实现相关

    cs