flask源码走读
Flask-Origin
源码版本
一直想好好理一下flask的实现,这个项目有Flask 0.1版本源码并加了注解,挺清晰明了的,我在其基础上完成了对Werkzeug的理解部分,大家如果想深入学习的话,可以参考werkzeug_flow.md.
阅读前
为了更容易理解Flask的实现原理,你需要对WSGI协议以及HTTP协议有一些了解,建议先简单浏览下面的基本知识:
Flask内部实现大量依赖于Werkzeug,包括请求和响应对象,路由匹配,URL生成等等,你可以阅读Werkzeug的文档来深入了解这些内容的具体实现。另外,如果你对模板渲染部分的内容感兴趣,也可以考虑阅读Jinja2文档:
werkzeug源码阅读,可以参考下面的函数打断点,再测试一个请求,理清过程。
其实可以参考简化后web服务实现思路,socket建立后,监听recv到的请求信息(no_wsgi_dome.no_wsgi.Socket._handle)并解析,然后调用相应的app.route对应的view_func.整个过程可以大致分为两部分:
1. app-> werkzeug-> http-> socket 启动端口监听,注册各种方法.
2. socket recv到请求-> 初始化RequestHandlerClass-> 调用Flask.__call__, wsgi_app在请求上下文中执行预处理方法,视图方法,后响应方法等.
flask启动流程,只追溯了app到http再到socket的启动,主流程就是BaseWSGIServer初始化调用了HTTPServer的初始化,进而初始化了BaseServer,在socketserver上启动了服务开始监听端口:
flask.Flask.run -> werkzeug.serving.run_simple ->
werkzeug.serving.run_simple.inner ->werkzeug.serving.make_server ->
BaseWSGIServer->HTTPServer.__init__(self, get_sockaddr(host, int(port),self.address_family), handler) ->
BaseServer.__init__(self, server_address, RequestHandlerClass) ->
werkzeug.serving.run_simple.inner.srv.serve_forever() ->
socketserver.BaseServer.serve_forever 建立socket服务开始监听,当ready也就是有recv到请求时开始 _handle_request_noblock
flask处理请求流程,追溯了socket接受到请求后触发app处理请求的主流程:
curl发出请求->socket接受到请求 ->
SocketServer.BaseServer.serve_forever._handle_request_noblock ->
SocketServer.BaseServer.process_request ->
SocketServer.BaseServer.finish_request ->
socketserver.BaseServer.__init__:self.RequestHandlerClass(request, client_address, self) ->
这里要找出RequestHandlerClass是如何初始化的,它的真身是什么 ->
socketserver.TCPServer.__init__:BaseServer.__init__(self, server_address, RequestHandlerClass) ->
http.server.HTTPServer(未重写__init__) ->
werkzeug.serving.BaseWSGIServer:HTTPServer.__init__(self, get_sockaddr(host, int(port),self.address_family), handler) (此处handler就是WSGIRequestHandler) ->
RequestHandlerClass的真身已经找到,就是WSGIRequestHandler 也就是说每次请求来了都初始化一个WSGIRequestHandler去处理 ->
处理的入口应该是werkzeug.serving.WSGIRequestHandler.handle可是简单一看并没找到是如何开始处理请求的->
往它的父类BaseHTTPRequestHandler中找也没有 ->
再往上socketserver.StreamRequestHandler ->
找到了SocketServer.BaseRequestHandler.__init__:try:self.handle() 关键点 ->
开始调用子类WSGIRequestHandler中的handle方法 ->
werkzeug.serving.WSGIRequestHandler (注意handler和handle_one_request,WSGIRequestHandler重载了BaseHTTPServer.BaseHTTPRequestHandler中的方法,BaseHTTPRequestHandler由重载了 SocketServer.BaseRequestHandler )->
werkzeug.serving.WSGIRequestHandler.handle_one_request调用werkzeug.serving.WSGIRequestHandler.run_wsgi 开始处理请求 ->
run_wsgi.execute(self.server.app)将请求交予app来处理 ->
flask.Flask.__call__ ->
flask.Flask.wsgi_app 开始app内的流程,交由wsgi_app在请求上下文中执行预处理方法,视图方法,后响应方法等。
可以看到实现过程中Server,Handler用到了继承并重载,层层包装了web服务
BaseWSGIServer继承了HTTPServer重写了BaseServer.serve_forever(包了一层),
HTTPServer继承了TCPServer重写了server_bind,
TCPServer继承了BaseServer重写了server_bind,
主要思路要理清socket接受接请求后如何用请求触发调用app,这里主要是SocketServer.BaseRequestHandler.__init__:try:self.handle() 这个__init__才是处理请求真正开始的地方.
进一步
- web的最原始的实现见 no_wsgi_dome ,不使用werkzeug,不使用wsgi约束,只是用socket如何实现http服务.这个对理解wsgi对http以及socket的封装有很好的借鉴意义.
- 补充了本地上下文相关的本地线程、本地堆栈、本地代理,并写了个LocalProxy_dome.py 辅助理解flask中是如何使用LocalProxy的。
- 我通过打断点,理通了app的启动和接受请求到处理请求的过程,可以参考werkzeug_flow.md配合flask_dome.py并手动打断点尝试一下.
- 根据下面的提示,自己理一下吧.
- Flask中的请求响应循环
- 路由系统
- 本地上下文
- 请求与响应对象
- session
- 蓝本
- 模板渲染
最后,要是觉得不错的话,点个赞支持一下吧,相关源码都放到了 我的github.
flask源码走读的更多相关文章
- Python Web Flask源码解读(三)——模板渲染过程
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- flask源码分析
本flask源码分析不间断更新 而且我分析的源码全是我个人觉得是很beautiful的 1 flask-login 1.1 flask.ext.login.login_required(func),下 ...
- Flask源码学习—config配置管理
自己用Flask做了一个博客(www.hbnnlove.sinaapp.com),之前苦于没有对源码解析的文档,只能自己硬着头皮看.现在我把我自己学习Flask源码的收获写出来,也希望能给后续要学习F ...
- Apache Spark源码走读之23 -- Spark MLLib中拟牛顿法L-BFGS的源码实现
欢迎转载,转载请注明出处,徽沪一郎. 概要 本文就拟牛顿法L-BFGS的由来做一个简要的回顾,然后就其在spark mllib中的实现进行源码走读. 拟牛顿法 数学原理 代码实现 L-BFGS算法中使 ...
- Apache Spark源码走读之16 -- spark repl实现详解
欢迎转载,转载请注明出处,徽沪一郎. 概要 之所以对spark shell的内部实现产生兴趣全部缘于好奇代码的编译加载过程,scala是需要编译才能执行的语言,但提供的scala repl可以实现代码 ...
- Apache Spark源码走读之13 -- hiveql on spark实现详解
欢迎转载,转载请注明出处,徽沪一郎 概要 在新近发布的spark 1.0中新加了sql的模块,更为引人注意的是对hive中的hiveql也提供了良好的支持,作为一个源码分析控,了解一下spark是如何 ...
- Apache Spark源码走读之7 -- Standalone部署方式分析
欢迎转载,转载请注明出处,徽沪一郎. 楔子 在Spark源码走读系列之2中曾经提到Spark能以Standalone的方式来运行cluster,但没有对Application的提交与具体运行流程做详细 ...
- twitter storm 源码走读之5 -- worker进程内部消息传递处理和数据结构分析
欢迎转载,转载请注明出处,徽沪一郎. 本文从外部消息在worker进程内部的转化,传递及处理过程入手,一步步分析在worker-data中的数据项存在的原因和意义.试图从代码实现的角度来回答,如果是从 ...
- 用尽洪荒之力学习Flask源码
WSGIapp.run()werkzeug@app.route('/')ContextLocalLocalStackLocalProxyContext CreateStack pushStack po ...
随机推荐
- 简要的谈一谈我对CSS中长度单位的理解
CSS中的长度单位目前分为两种,分别是绝对长度和相对长度.绝对长度单位包括: in:英寸 cm:厘米 mm:毫米 pt:磅(1磅等于1/72英寸) pc:pica(1pica等于12磅) 以上五个就是 ...
- 用Java编写银行存钱取钱
const readline = require('readline-sync')//引用readline-sync let s = 2;//错误的次数 for (let i = 0; i < ...
- 手写redis客户端
一.RESP通信协议 Redis Serialization Protocol (Redis序列化协议). 特点:容易实现.解析快.可读性强 以\r\n分割数据. 二.撸代码 package com. ...
- CSS之元素
CSSS书写位置 内嵌式 <head> <style type = "text/css"> **** </style> </head> ...
- c#一个日志类(log4net)
这个类就是对log4net的使用,就不多说了,但是看见网上的一个封装,自己用了下,感觉还不错,直接记录在这里.把自己使用的类直接贴出来. using log4net; using log4net.Co ...
- #leetcode刷题之路12-整数转罗马数字
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值I 1V 5X 10L 50C 100D 500M 1000 例如, 罗马数字 2 写做 II ,即为两个并列的 1.12 ...
- C++继承细节 -2
继承与动态内存分配 //基类定义 class BaseClass { private: char *label; public: BaseClass() {} BaseClass(const char ...
- html 让table表格中的td单元格内容过长显示为固定长度,多余部分用省略号代替?
<th class="wrap">商品名</th> .wrap{ width: 150px; //设置需要固定的宽度 white-space: nowrap ...
- CentOS 6.8安装Ceph
机器规划 IP 主机名 角色 10.101.0.1 ceph01 mon admin mds 10.101.0.2 ceph02 ods 10.101.0.3 ceph03 ods 10.101.0. ...
- 爬虫——Scrapy框架案例二:阳光问政平台
阳光热线问政平台 URL地址:http://wz.sun0769.com/index.php/question/questionType?type=4&page= 爬取字段:帖子的编号.投诉类 ...