这段时间想重新写个自己的博客系统,又正好在看一些框架源码,然后就想要不顺便写个小框架吧,既然想写框架,要不再顺便写个orm吧,再写个小的异步Server吧。。事实证明饭要一口一口吃

先梳理一下flask工作的整个流程吧。

首先flask是符合wsgi协议的,那么也就是说,flask要实现一个可以callable的object给web server。

那么什么是wsgi? 简而言之,wsgi就是把框架和web Server协同工作的一个协议,规范。流程大概是:当有请求来的时候, web Server调用一个callable object(函数,类,实现__call__的实例等等), 传入(environ, start_response), 其中environ是一个字典,包含了请求相关的所有信息,比如请求路径,参数;start_response是一个函数。在这个callable object中会调用start_response,返回http状态码,headers等信息给web server, callable object本身则会返回具体的内容给web server, 返回的object必须是可迭代的。当有callable object本身返回的内容时候, 就会把start_response返回的内容和callable object返回的内容写入缓冲区并且刷新。

  那么flask 给web server的callable object是什么?

Class Flask:
...
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)

可以看到,是一个实现了__call__的实例,web server 调用Flask的实例,传入environ和start_response,那么这个self.wsgi_app是什么东西?

     def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a
reference to the class. So instead of doing this:: app = MyMiddleware(app) It's a better idea to do this instead:: app.wsgi_app = MyMiddleware(app.wsgi_app) Then you still have the original application object around and
can continue to call methods on it. .. versionchanged:: 0.7
The behavior of the before and after request callbacks was changed
under error conditions and a new callback was added that will
always execute at the end of the request, independent on if an
error ocurred or not. See :ref:`callbacks-and-errors`. :param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
"""
with self.request_context(environ):
try:
response = self.full_dispatch_request()
except Exception, e:
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)

在文档里面第一句已经说明了“The actual WSGI application...”这个才是真正的callable object。那么这个真正的callable object会做什么事情?

首先,会打开self.request_context(envion),

class RequestContext(object):

    def __init__(self, app, environ):
self.app = app
self.request = app.request_class(environ)
self.url_adapter = app.create_url_adapter(self.request)
self.g = app.request_globals_class()
self.flashes = None
self.session = None self.preserved = False
self._after_request_functions = [] self.match_request() blueprint = self.request.blueprint
if blueprint is not None:
# better safe than sorry, we don't want to break code that
# already worked
bp = app.blueprints.get(blueprint)
if bp is not None and blueprint_is_module(bp):
self.request._is_old_module = True def match_request(self):
try:
url_rule, self.request.view_args = \
self.url_adapter.match(return_rule=True)
self.request.url_rule = url_rule
except HTTPException, e:
self.request.routing_exception = e def push(self):
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
app_ctx = self.app.app_context()
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)
else:
self._implicit_app_ctx_stack.append(None) _request_ctx_stack.push(self)
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session() def pop(self, exc=None):
app_ctx = self._implicit_app_ctx_stack.pop() clear_request = False
if not self._implicit_app_ctx_stack:
self.preserved = False
if exc is None:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)
clear_request = True rv = _request_ctx_stack.pop()
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
% (rv, self) # get rid of circular dependencies at the end of the request
# so that we don't require the GC to be active.
if clear_request:
rv.request.environ['werkzeug.request'] = None # Get rid of the app as well if necessary.
if app_ctx is not None:
app_ctx.pop(exc) def __enter__(self):
self.push()
return self def __exit__(self, exc_type, exc_value, tb):
if self.request.environ.get('flask._preserve_context') or \
(tb is not None and self.app.preserve_context_on_exception):
self.preserved = True
else:
self.pop(exc_value)

从__enter__可以看到,首先会把当前请求推入栈,这个是什么栈?有什么用呢?

  简而言之,这个栈就是flask根据每次请求时候,为每个请求所创建的线程/协程的id所创建的一个自己的threadlocal,在这个threadlocal中所实现的一个栈结构,会把不同的请求隔离开来,每次请求时候把当前请求所在的app,request推入栈,就可以在全局可用了。

 把current_app,request推入栈之后,就根据请求的方法,路径等参数分发请求,找到用@app.route('...')注册的函数,生成response。

但是这里有个问题,就是根据wsgi协议,返回的结果应该是个可迭代的结果,而且这时候start_response还没执行,状态码和其他头部信息还都没有。这个就是make_response的事情了,

    def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception, e:
rv = self.handle_user_exception(e)
response = self.make_response(rv)
response = self.process_response(response)
request_finished.send(self, response=response)
return response

make_response会把返回结果包装一下。

 def make_response(self, rv):
if isinstance(rv, basestring):
rv = self.response_class(rv, headers=headers, status=status)
headers = status = None
else:
rv = self.response_class.force_type(rv, request.environ) if status is not None:
if isinstance(status, basestring):
rv.status = status
else:
rv.status_code = status
if headers:
rv.headers.extend(headers) return rv

make_response会利用class Response(BaseResponse)把view function返回的结果包装一下,

class Response(ResponseBase):
default_mimetype = 'text/html' class BaseResponse(object):
automatically_set_content_length = True def __init__(self, response=None, status=None, headers=None,
.... @classmethod
def force_type(cls, response, environ=None):
... @classmethod
def from_app(cls, app, environ, buffered=False):
... def _get_status_code(self):
return self._status_code def __call__(self, environ, start_response): app_iter, status, headers = self.get_wsgi_response(environ)
start_response(status, headers)
return app_iter

当执行Response的实例时候,在__call__里面会执行start_response, 同时返回一个可迭代的对象

这就是flask的执行逻辑

参考资料:

PEP 333 -- Python Web Server Gateway Interface v1.0

flask源码剖析的更多相关文章

  1. flask源码剖析系列(系列目录)

    flask源码剖析系列(系列目录) 01 flask源码剖析之werkzurg 了解wsgi 02 flask源码剖析之flask快速使用 03 flask源码剖析之threading.local和高 ...

  2. 08 Flask源码剖析之flask拓展点

    08 Flask源码剖析之flask拓展点 1. 信号(源码) 信号,是在flask框架中为我们预留的钩子,让我们可以进行一些自定义操作. pip3 install blinker 2. 根据flas ...

  3. flask 源码剖析

    flask 上下文管理源码流程及涉及的部分技术点 [flask源码梳理]之一  偏函数_mro [flask源码梳理]之二  面向对象中__setattr__ [flask源码梳理]之三  Local ...

  4. Flask源码剖析详解

    1. 前言 本文将基于flask 0.1版本(git checkout 8605cc3)来分析flask的实现,试图理清flask中的一些概念,加深读者对flask的理解,提高对flask的认识.从而 ...

  5. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  6. 05 flask源码剖析之配置加载

    05 Flask源码之:配置加载 目录 05 Flask源码之:配置加载 1.加载配置文件 2.app.config源码分析 3.from_object源码分析 4. 总结 1.加载配置文件 from ...

  7. 06 flask源码剖析之路由加载

    06 Flask源码之:路由加载 目录 06 Flask源码之:路由加载 1.示例代码 2.路由加载源码分析 1.示例代码 from flask import Flask app = Flask(__ ...

  8. 07 flask源码剖析之用户请求过来流程

    07 Flask源码之:用户请求过来流程 目录 07 Flask源码之:用户请求过来流程 1.创建ctx = RequestContext对象 2. 创建app_ctx = AppContext对象 ...

  9. flask源码剖析--请求流程

    想了解这篇里面的内容,请先去了解我另外一篇博客Flask上下文 在了解flask之前,我们需要了解两个小知识点 偏函数 import functools def func(a1,a2): print( ...

随机推荐

  1. java删除文件及其目录

    1.删除指定文件路径 public @ResponseBody String deleteFiles(HttpServletRequest request) { log.info(this.getCl ...

  2. (原)tensorflow中使用指定的GPU及GPU显存

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/6591923.html 参考网址: http://stackoverflow.com/questions ...

  3. MySQL占用IO过高解决方案【转】

    1.日志产生的性能影响: 由于日志的记录带来的直接性能损耗就是数据库系统中最为昂贵的IO资源.MySQL的日志包括错误日志(ErrorLog),更新日志(UpdateLog),二进制日志(Binlog ...

  4. c# 取本地ip地址

    public static System.Net.IPAddress[] GetIpAddress() { string hostName = System.Net.Dns.GetHostName() ...

  5. Ex 6_20 最优二叉搜索树..._第六次作业

    假设关键字的总数为n,用c[i,j]表示第i个关键字到第j个关键字的最优二叉查找树的代价,我们的目标是求c[0,n-1].要求c[i,j],首先要从第i个关键字到第j个关键字中选一个出来作为根结点,选 ...

  6. https协议的接口测试

    用jmeter测试https接口: 和传统的http协议套路不太一样. 注意细节: 1.取样器正常选择http请求,端口号:为443(具体根据接口文档我刚开始用的80端口所以错了) 2.请求方法,一定 ...

  7. PYTHON-模块time&datetime+ 目录规范

    1.目录规范 ***** (1)文件夹的规范写法 bin 可执行文件 conf 配置文件 core 主要业务逻辑 db 数据文件 lib 库 (公共代码 第三方模块) log 日志文件 readme ...

  8. Slick.js+Animate.css 结合让网页炫动起来

    一个代码示例: html部分 <link rel='stylesheet prefetch' href='//cdnjs.cloudflare.com/ajax/libs/animate.css ...

  9. Codeforces 407B Long Path(好题 DP+思维)

    题目链接:http://codeforces.com/problemset/problem/407/B 题目大意:一共n+1个房间,一个人从1走到n+1,每次经过房间都会留下一个标记,每个房间有两扇门 ...

  10. ural1989 单点更新+字符串hash

    正解是双哈希,不过一次哈希也能解决.. 然后某个数字就对应一个字符串,虽然有些不同串对应同一个数字,但是概率非常小,可以忽略不计.从左到右.从右到左进行两次hash,如果是回文串,那么对应的整数必定存 ...