欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

web.py源码阅读(一) WSGI协议篇

程序员文章站 2022-07-15 13:26:05
...

WSGI协议简述

WSGI的全称是Python Web Server Gateway Interface, 看这个名称比较晦涩难懂,下面简单解释一下为什么会有WSGI这个东西。

一个网站的服务端通常分为两部分,web应用和服务器。 其中web应用通常又包括web框架和业务逻辑代码。 服务器有很多(比如uWSGI,Gunicorn等),web框架也有很多(django,flask等)。为了让框架端与服务器端解耦,即实现基于任意的框架开发的web应用可以部署在任意的web服务器上,框架与服务器之间需要制定一个规范,这个规范就是WSGI。 二者的关系大体可以用下图这个模型来表示:
web.py源码阅读(一) WSGI协议篇

web.py中WSGI的实现

  • Web框架端(Application端):

    WSGI协议规定,在web框架这一端必须定义一个可以接受两个参数的可调用对象(显然这两个参数肯定是WSGI Server传过来的),这个可调用对象可以是函数,类方法,类,以及一个包含call的类实例。在web.py源码中这个函数位于application.py文件下的application类里面的名为wsgi的函数,关于这个函数的具体实现细节以及参数的含义会在以后的文章中详细介绍。现在思考一个问题,WSGI协议为什么要求web框架端必须返回一个可调用对象呢?(答案是WSGI Server需要调用application端(web框架端),把request的一些信息传给application,然后application会返回response给server端)


  • WSGIServer端:

    WSGI Server端首先应该是一个服务器,其次还要实现WSGI协议。实现WSGI协议无非就是构造出需要传给web端的两个参数:environ环境变量和start_response这个函数类型的参数,然后在一次request到来的时候WSGI Server把这两个参数塞给Application端传过来的可调用对象完成调用。关于environ环境变量的构造和start_reponse函数的具体实现在后面的源码剖析中会仔细介绍,在这里先提一下,environ在web.py源码中的实现位于web/wsgiserver/init.py的HttpConnection类,而start_response这个函数的实现位于web/wsgiserver/init.py的HttpRequest类,至于WSGI Server调用Application端实现的wsgi函数则是发生在web/wsgiserver/init.py的HttpRequest类中的respond函数里面。


多说一句:Application端实现的wsgi函数会在服务器一开始启动的时候就会被注册到WSGI Server中,而具体的调用则是发生在有客户端的request到来的时候。(不理解的话可以忽略,在后面的源码详细分析中到了这个地方还是会再强调一遍)

web.py的启动流程

上面啰啰嗦嗦的算是把WSGI服务器与框架的关系介绍完了,下面打算再写一下web.py源码里面服务器的启动流程,这也是阅读源码的第一课,即当你基于web.py框架写了一个hello world程序,然后将服务run起来时,这段时间在底层源码中经历了一段怎样的故事。当然还是主要聚焦在数据的流转过程,至于每个类或者函数的具体实现会在后面的源码详细分析中一一到来。

我先贴一个hello world的程序,也是web.py的官网给出的demo事例。
web.py源码阅读(一) WSGI协议篇

接下来是一张鄙人画的并不专业的调用关系图:
web.py源码阅读(一) WSGI协议篇
下面就跟着上面这张图过一遍底层的执行过程:

        demo.py就是我们写的hello world程序,核心的代码就两行,一个是new出一个app实例,然后执行app.run()方法启动服务。好了,下面全是框架源码部分了。首先确定app.run()这个方法位于web/application.py下的application类,在这个run函数里又调用了web/wsgi.py里面的runwsgi()函数,依据是run函数中的return wsgi.runwsgi(self.wsgifunc(*middleware)),wsgifunc这个函数也是在application里面,这个函数里面还定义了wsgi函数,这个wsgi函数就是application实现了wsgi协议的那个函数,所以runwsgi接收的参数func其实就是那个wsgi函数(可调用对象)。接下来进入runwsgi函数,runwsgi里面的核心就是这句 return httpserver.runsimple(func,server_address),含义可以大概猜到就是用application传过来的wsgi函数和address这两个参数启动一个wsgi server,但是到这里我们还不清楚这个wsgi server是怎么来的。接下来进入httpserver.py这个文件一探究竟(位于web/application.py),找到runsimple函数,核心是 server = WSGIServer(server_address, func) server.start()这两行代码,到此我们清楚了,启动时的wsgi server 是一个名为WSGIServer的函数返回的实例,注意这里的WSGIServer是一个函数,不是一个类,WSGIServer这个函数位置就在runsimple函数下面。按图索骥接着进入WSGIServer这个函数,代码只有两句

 from wsgiserver import CherryPyWSGIServer
 return CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")

很清楚看到我们启动的这个server就是CherryPyWSGIServer的实例, web/wsgiserver这个包的init文件里有CherryPyWSGIServer这个WSGI服务器的完整实现,有兴趣的可以继续深挖一下。

好了,第一篇到这里,就到这里,启动流程梳理完了,if have any questions, please read this article repeatedly or leave a message for me.