Django運行方式及處理流程總結


學了django有一段時間了,也沒深入了解過它內部的實現,正好看到一篇介紹django運行原理的,有圖有代碼,深度好文,值得收藏。
                         -- xxmcf 2015.09.28 22:29
原文鏈接: 

前在網上看過一些介紹Django處理請求的流程和Django源碼結構的文章,覺得了解一下這些內容對開發Django項目還是很有幫助的。所以,我按照自己的邏輯總結了一下Django項目的運行方式和對Request的基本處理流程。


一、Django的運行方式

運行Django項目的方法很多,這里主要介紹一下常用的方法。一種是在開發和調試中經常用到runserver方法,使用Django自己的web server;另外一種就是使用fastcgi,uWSGIt等協議運行Django項目,這里以uWSGIt為例。

1、runserver方法

runserver方法是調試Django時經常用到的運行方式,它使用Django自帶的WSGI Server運行,主要在測試和開發中使用,使用方法如下:

 

[python] view plain copy
  1. Usage: manage.py runserver [options] [optional port number, or ipaddr:port]  
  2. # python manager.py runserver    # default port is 8000  
  3. # python manager.py runserver 8080  
  4. # python manager.py runserver 127.0.0.1:9090  

看一下manager.py的源碼,你會發現上面的命令其實是通過Django的execute_from_command_line方法執行了內部實現的runserver命令,那么現在看一下runserver具體做了什么。。

    1). 解析參數,並通過django.core.servers.basehttp.get_internal_wsgi_application方法獲取wsgi handler;

    2). 根據ip_address和port生成一個WSGIServer對象,接受用戶請求

 

[python] view plain copy
  1. def get_internal_wsgi_application():  
  2.     """ 
  3.     Loads and returns the WSGI application as configured by the user in 
  4.     ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout, 
  5.     this will be the ``application`` object in ``projectname/wsgi.py``. 
  6.   
  7.     This function, and the ``WSGI_APPLICATION`` setting itself, are only useful 
  8.     for Django's internal servers (runserver, runfcgi); external WSGI servers 
  9.     should just be configured to point to the correct application object 
  10.     directly. 
  11.   
  12.     If settings.WSGI_APPLICATION is not set (is ``None``), we just return 
  13.     whatever ``django.core.wsgi.get_wsgi_application`` returns. 
  14.   
  15.     """  
  16.     from django.conf import settings  
  17.     app_path = getattr(settings, 'WSGI_APPLICATION')  
  18.     if app_path is None:  
  19.         return get_wsgi_application()  
  20.    
  21.     return import_by_path(  
  22.         app_path,  
  23.         error_prefix="WSGI application '%s' could not be loaded; " % app_path  
  24.     )  

通過上面的代碼我們可以知道,Django會先根據settings中的WSGI_APPLICATION來獲取handler;在創建project的時候,Django會默認創建一個wsgi.py文件,而settings中的WSGI_APPLICATION配置也會默認指向這個文件。看一下這個wsgi.py文件,其實它也和上面的邏輯一樣,最終調用get_wsgi_application實現。


2、uWSGI方法

uWSGI+Nginx的方法是現在最常見的在生產環境中運行Django的方法,本人的博客也是使用這種方法運行,要了解這種方法,首先要了解一下WSGI和uWSGI協議。

WSGI,全稱Web Server Gateway Interface,或者Python Web Server Gateway Interface,是為Python語言定義的Web服務器和Web應用程序或框架之間的一種簡單而通用的接口,基於現存的CGI標准而設計的。WSGI其實就是一個網關(Gateway),其作用就是在協議之間進行轉換。(PS: 這里只對WSGI做簡單介紹,想要了解更多的內容可自行搜索)

uWSGI是一個Web服務器,它實現了WSGI協議、uwsgi、http等協議。注意uwsgi是一種通信協議,而uWSGI是實現uwsgi協議和WSGI協議的Web服務器。uWSGI具有超快的性能、低內存占用和多app管理等優點。以我的博客為例,uWSGI的xml配置如下:

uwsgi>  

  •     <!-- 端口 -->  
  •     <socket>:7600</socket>  
  •     <stats>:40000</stats>  
  •     <!-- 系統環境變量 -->  
  •     <env>DJANGO_SETTINGS_MODULE=geek_blog.settings</env>  
  •     <!-- 指定的python WSGI模塊 -->  
  •     <module>django.core.handlers.wsgi:WSGIHandler()</module>  
  •     <processes>6</processes>  
  •     <master />  
  •     <master-as-root />  
  •     <!-- 超時設置 -->  
  •     <harakiri>60</harakiri>  
  •     <harakiri-verbose/>  
  •     <daemonize>/var/app/log/blog/uwsgi.log</daemonize>  
  •     <!-- socket的監聽隊列大小 -->  
  •     <listen>32768</listen>  
  •     <!-- 內部超時時間 -->  
  •     <socket-timeout>60</socket-timeout>  
  • </uwsgi>  


  • uwsgi --pidfile=/var/run/geek-blog.pid -x uwsgi.xml --uid blog --gid nogroup  
uWSGI和Nginx一起使用的配置方法就不在這里說明了,網上教程很多,需要的可以自行搜索。

 


二、HTTP請求處理流程

Django和其他Web框架一樣,HTTP的處理流程基本類似:接受request,返回response內容。Django的具體處理流程大致如下圖所示:

1、加載project settings

創建WSGIServer之前會執行下面的引用:

 

[python] view plain copy
  1. from django.conf import settings  


上面引用在執行時,會讀取os.environ中的DJANGO_SETTINGS_MODULE配置,加載項目配置文件,生成settings對象。所以,在manager.py文件中你可以看到,在獲取WSGIServer之前,會先將project的settings路徑加到os路徑中。

2、創建WSGIServer

 

 

[python] view plain copy
  1. def run(addr, port, wsgi_handler, ipv6=False, threading=False):  
  2.     server_address = (addr, port)  
  3.     if threading:  
  4.         httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})  
  5.     else:  
  6.         httpd_cls = WSGIServer  
  7.     httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)  
  8.     # Sets the callable application as the WSGI application that will receive requests  
  9.     httpd.set_app(wsgi_handler)  
  10.     httpd.serve_forever()  


其中,WSGIServer繼承自wsgiref.simple_server.WSGIServer,而WSGIRequestHandler繼承自wsgiref.simple_server.WSGIRequestHandler,wsgiref是Python標准庫給出的WSGI的參考實現。其源碼可自行到wsgiref參看,這里不再細說。

3、處理Request

第二步中說到的application,在Django中一般是django.core.handlers.wsgi.WSGIHandler對象,WSGIHandler繼承自django.core.handlers.base.BaseHandler,這個是Django處理request的核心邏輯,它會創建一個WSGIRequest實例,而WSGIRequest是從http.HttpRequest繼承而來

4、返回Response

上面提到的BaseHandler中有個get_response方法,該方法會先加載Django項目的ROOT_URLCONF,然后根據url規則找到對應的view方法(類),view邏輯會根據request實例生成並返回具體的response。

在Django返回結果之后,第二步中提到wsgiref.handlers.BaseHandler.run方法會調用finish_response結束請求,並將內容返回給用戶。

三、Django處理Request的詳細流程

Django流程圖1

Django流程圖2

    1. 用戶通過瀏覽器請求一個頁面

    2. 請求到達Request Middlewares,中間件對request做一些預處理或者直接response請求

    3. URLConf通過urls.py文件和請求的URL找到相應的View

    4. View Middlewares被訪問,它同樣可以對request做一些處理或者直接返回response

    5. 調用View中的函數

    6. View中的方法可以選擇性的通過Models訪問底層的數據

    7. 所有的Model-to-DB的交互都是通過manager完成的

    8. 如果需要,Views可以使用一個特殊的Context

    9. Context被傳給Template用來生成頁面

    a. Template使用Filters和Tags去渲染輸出

    b. 輸出被返回到View

    c. HTTPResponse被發送到Response Middlewares

    d. 任何Response Middlewares都可以豐富response或者返回一個完全不同的response

    e. Response返回到瀏覽器,呈現給用戶

1、Middleware(中間件)

Middleware是在Django BaseHandler的load_middleware方法執行時加載的,加載之后會建立四個列表作為處理器的實例變量:

    _request_middleware:process_request方法的列表

    _view_middleware:process_view方法的列表

    _response_middleware:process_response方法的列表

    _exception_middleware:process_exception方法的列表

 

[python] view plain copy
  1. MIDDLEWARE_CLASSES = (  
  2.     'django.middleware.cache.UpdateCacheMiddleware',  
  3.     'django.middleware.common.CommonMiddleware',  
  4.     'django.middleware.cache.FetchFromCacheMiddleware',  
  5.     'django.contrib.sessions.middleware.SessionMiddleware',  
  6.     'django.middleware.csrf.CsrfViewMiddleware',  
  7.     'django.contrib.auth.middleware.AuthenticationMiddleware',  
  8.     'django.contrib.messages.middleware.MessageMiddleware',  
  9.     'django.middleware.locale.LocaleMiddleware',  
  10.     'geek_blog.middlewares.MobileDetectionMiddleware',    # 自定義的Middleware  
  11. )  

 

Django項目的安裝並不強制要求任何中間件,如果你願意,MIDDLEWARE_CLASSES可以為空。中間件出現的順序非常重要:在request和view的處理階段,Django按照MIDDLEWARE_CLASSES中出現的順序來應用中間件,而在response和exception異常處理階段,Django則按逆序來調用它們。也就是說,Django將MIDDLEWARE_CLASSES視為view函數外層的順序包裝子:在request階段按順序從上到下穿過,而在response則反過來。以下兩張圖可以更好地幫助你理解:

 

Django Middleware流程1

Django Middleware流程圖2

2、URLConf(URL映射)

第一個匹配的view。

DjangoBook了解。

3、Template(模板)


uWSGI Web服務器介紹

wsgiref源碼分析

用Python寫一個簡單的Web框架

Django 結構及處理流程分析

PS: 以上代碼和內容都是基於Django 1.6.5版本,其他版本可能與其不同,請參考閱讀。

Over!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM