天天看點

web.py源碼閱讀(一) WSGI協定篇

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.

繼續閱讀