摘要:另外,如果你对模板渲染部分的内容感兴趣,也可以考虑阅读文档文档文档源码阅读,可以参考下面的函数打断点,再测试一个请求,理清过程。
Flask-Origin 源码版本
一直想好好理一下flask的实现,这个项目有Flask 0.1版本源码并加了注解,挺清晰明了的,我在其基础上完成了对Werkzeug的理解部分,大家如果想深入学习的话,可以参考werkzeug_flow.md.
阅读前为了更容易理解Flask的实现原理,你需要对WSGI协议以及HTTP协议有一些了解,建议先简单浏览下面的基本知识:
PEP 0333和PEP 3333(WSGI实现)
HTTP概述
Flask内部实现大量依赖于Werkzeug,包括请求和响应对象,路由匹配,URL生成等等,你可以阅读Werkzeug的文档来深入了解这些内容的具体实现。另外,如果你对模板渲染部分的内容感兴趣,也可以考虑阅读Jinja2文档:
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_noblockflask处理请求流程,追溯了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.文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/43535.html
摘要:今天对象在学习时发现对象的方法并不能清理一级缓存同一下相同查询条件返回的结果还是旧值。测试代码如下上网搜索网上搜索找到了相同问题并没有人解答。例如查看官方文档实例有一个本地缓存在执行和时被清理。要明确地关闭它获取打算做更多的工作你可以调用。 今天对象在学习 Mybatis 时发现 org.apache.ibatis.session.SqlSession 对象的 clearCache()...
摘要:源码走读在运行时发生了什么标签空格分隔原文作者是,原文地址是在这篇博文中我将回答一下问题运行一个期间内部发生了什么开始首先从并检查代码。这个发生在和行之间。如果一个错误发生,它会被记录,然后程序退出。这些覆盖了在客户端里面发生了什么。 Docker 源码走读 - 在运行 Docker run 时发生了什么? 标签(空格分隔): Docker 原文作者是 Frank Scho...
摘要:拦截器注册配置拦截器注册配置通过可以发现中的的清理工作没有得到执行。只会在出现异常,返回或正常执行结束才会从索引依次往前执行。 发现问题 最近在进行压测发现,有一些接口时好时坏,通过sentry日志平台及sky walking平台跟踪发现,用户张三获取到的用户上下文确是李四。 代码走读 用户登录下上文 /** * 用户登录下上文 * * @author : jamesfu * @da...
摘要:如果当前不是主线程则直接调用,如果是线程则创建一个加入到后台的一个队列,最终由中的一个线程池去调用。抛出线程状态非法异常。 while (clazz != null) {String name = clazz.getName();if (name.startsWith(java.) || name.starts...
阅读 1868·2021-09-23 11:21
阅读 1661·2019-08-29 17:27
阅读 1006·2019-08-29 17:03
阅读 695·2019-08-29 15:07
阅读 1887·2019-08-29 11:13
阅读 2353·2019-08-26 12:14
阅读 870·2019-08-26 11:52
阅读 1700·2019-08-23 17:09