分析Python的Django框架的运行方式及处理流程
之前在网上看过一些介绍django处理请求的流程和django源码结构的文章,觉得了解一下这些内容对开发django项目还是很有帮助的。所以,我按照自己的逻辑总结了一下django项目的运行方式和对request的基本处理流程。
一、django的运行方式
运行django项目的方法很多,这里主要介绍一下常用的方法。一种是在开发和调试中经常用到runserver方法,使用django自己的web server;另外一种就是使用fastcgi,uwsgit等协议运行django项目,这里以uwsgit为例。
1、runserver方法
runserver方法是调试django时经常用到的运行方式,它使用django自带的wsgi server运行,主要在测试和开发中使用,使用方法如下:
usage: manage.py runserver [options] [optional port number, or ipaddr:port] # python manager.py runserver # default port is 8000 # python manager.py runserver 8080 # python manager.py runserver 127.0.0.1:9090
看一下manager.py的源码,你会发现上面的命令其实是通过django的execute_from_command_line方法执行了内部实现的runserver命令,那么现在看一下runserver具体做了什么。。
看了源码之后,可以发现runserver命令主要做了两件事情:
1). 解析参数,并通过django.core.servers.basehttp.get_internal_wsgi_application方法获取wsgi handler;
2). 根据ip_address和port生成一个wsgiserver对象,接受用户请求
get_internal_wsgi_application的源码如下: def get_internal_wsgi_application(): """ loads and returns the wsgi application as configured by the user in ``settings.wsgi_application``. with the default ``startproject`` layout, this will be the ``application`` object in ``projectname/wsgi.py``. this function, and the ``wsgi_application`` setting itself, are only useful for django's internal servers (runserver, runfcgi); external wsgi servers should just be configured to point to the correct application object directly. if settings.wsgi_application is not set (is ``none``), we just return whatever ``django.core.wsgi.get_wsgi_application`` returns. """ from django.conf import settings app_path = getattr(settings, 'wsgi_application') if app_path is none: return get_wsgi_application() return import_by_path( app_path, error_prefix="wsgi application '%s' could not be loaded; " % app_path )
通过上面的代码我们可以知道,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 xml配置的写法,也可以使用ini的方式。安装uwsgi和运行的命令如下:
sudo pip install 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
在通过django-admin.py创建project的时候,django会自动生成默认的settings文件和manager.py等文件,在创建wsgiserver之前会执行下面的引用:
from django.conf import settings
上面引用在执行时,会读取os.environ中的django_settings_module配置,加载项目配置文件,生成settings对象。所以,在manager.py文件中你可以看到,在获取wsgiserver之前,会先将project的settings路径加到os路径中。
2、创建wsgiserver
不管是使用runserver还是uwsgi运行django项目,在启动时都会调用django.core.servers.basehttp中的run()方法,创建一个django.core.servers.basehttp.wsgiserver类的实例,之后调用其serve_forever()方法启动http服务。run方法的源码如下:
def run(addr, port, wsgi_handler, ipv6=false, threading=false): server_address = (addr, port) if threading: httpd_cls = type(str('wsgiserver'), (socketserver.threadingmixin, wsgiserver), {}) else: httpd_cls = wsgiserver httpd = httpd_cls(server_address, wsgirequesthandler, ipv6=ipv6) # sets the callable application as the wsgi application that will receive requests httpd.set_app(wsgi_handler) httpd.serve_forever()
如上,我们可以看到:在创建wsgiserver实例的时候会指定http请求的handler,上述代码使用wsgirequesthandler。当用户的http请求到达服务器时,wsgiserver会创建wsgirequesthandler实例,使用其handler方法来处理http请求(其实最终是调用wsgiref.handlers.basehandler中的run方法处理)。wsgiserver通过set_app方法设置一个可调用(callable)的对象作为application,上面提到的handler方法最终会调用设置的application处理request,并返回response。
其中,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在处理request的时候其实做了很多事情,下面我们详细的过一下。首先给大家分享两个网上看到的django流程图:
django流程图1
django流程图2
上面的两张流程图可以大致描述django处理request的流程,按照流程图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返回到浏览器,呈现给用户
上述流程中最主要的几个部分分别是:middleware(中间件,包括request, view, exception, response),urlconf(url映射关系),template(模板系统),下面一一介绍一下。
1、middleware(中间件)
middleware并不是django所独有的东西,在其他的web框架中也有这种概念。在django中,middleware可以渗入处理流程的四个阶段:request,view,response和exception,相应的,在每个middleware类中都有rocess_request,process_view, process_response 和 process_exception这四个方法。你可以定义其中任意一个活多个方法,这取决于你希望该middleware作用于哪个处理阶段。每个方法都可以直接返回response对象。
middleware是在django basehandler的load_middleware方法执行时加载的,加载之后会建立四个列表作为处理器的实例变量:
- _request_middleware:process_request方法的列表
- _view_middleware:process_view方法的列表
- _response_middleware:process_response方法的列表
- _exception_middleware:process_exception方法的列表
django的中间件是在其配置文件(settings.py)的middleware_classes元组中定义的。在middleware_classes中,中间件组件用字符串表示:指向中间件类名的完整python路径。例如geekblog项目的配置:
middleware_classes = ( 'django.middleware.cache.updatecachemiddleware', 'django.middleware.common.commonmiddleware', 'django.middleware.cache.fetchfromcachemiddleware', 'django.contrib.sessions.middleware.sessionmiddleware', 'django.middleware.csrf.csrfviewmiddleware', 'django.contrib.auth.middleware.authenticationmiddleware', 'django.contrib.messages.middleware.messagemiddleware', 'django.middleware.locale.localemiddleware', 'geek_blog.middlewares.mobiledetectionmiddleware', # 自定义的middleware )
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映射)
如果处理request的中间件都没有直接返回response,那么django会去解析用户请求的url。urlconf就是django所支撑网站的目录。它的本质是url模式以及要为该url模式调用的视图函数之间的映射表。通过这种方式可以告诉django,对于这个url调用这段代码,对于那个url调用那段代码。具体的,在django项目的配置文件中有root_urlconf常量,这个常量加上根目录"/",作为参数来创建django.core.urlresolvers.regexurlresolver的实例,然后通过它的resolve方法解析用户请求的url,找到第一个匹配的view。
其他有关urlconf的内容,这里不再具体介绍,大家可以看djangobook了解。
3、template(模板)
大部分web框架都有自己的template(模板)系统,django也是。但是,django模板不同于mako模板和jinja2模板,在django模板不能直接写python代码,只能通过额外的定义filter和template tag实现。由于本文主要介绍django流程,模板内容就不过多介绍。
ps: 以上代码和内容都是基于django 1.6.5版本,其他版本可能与其不同,请参考阅读。
over!
上一篇: 蕹菜的功效与作用