基于QuickStart中的一个demo来分析

from flask import Flaskapp = Flask(__name__)@app.route("/")
def hello_world():return "<p>Hello, World!</p>"if __name__ == '__main__':app.run()

@app.route(“/”) 是一个接收参数的装饰器工厂函数,返回一个闭包了特定参数的装饰器

# flask/app.py L1288
def route(self, rule, **options):# 典型的注册装饰器def decorator(f):endpoint = options.pop("endpoint", None)# 此方法将url信息和view_func 注册到flask实例中self.add_url_rule(rule, endpoint, f, **options)return freturn decorator

关键在 self.add_url_rule(rule, endpoint, f, **options) 方法 下面代码做了一些精简

# flask/app.py L1178
@setupmethod
def add_url_rule(self,rule,endpoint=None,view_func=None,provide_automatic_options=None,**options
):# 此处endpoint是一个重要的概念 后续在路由部分会详细讲解 # 在flask中是 url_map和view_functions 关联的重要纽带# 每次请求来时 去url_map中搜索得到 endpoint,args 然后走 view_functions[endpoint](args) 拿到结果# 若不传 默认为view_func.__name__if endpoint is None:endpoint = _endpoint_from_view_func(view_func)options["endpoint"] = endpointmethods = options.pop("methods", None)# ...# 此处的 url_rule_class 是一个Flask类属性 值为 werkzeug.routing.Rule# 所以此处即为构建 werkzeug.routing.Rule 对象rule = self.url_rule_class(rule, methods=methods, **options)rule.provide_automatic_options = provide_automatic_options# 构建好的 rule对象保存到 url_map实例属性中 # url_map是werkzeug.routing.Map 对象# flask路由部分其实借助了werkzeug的能力self.url_map.add(rule)if view_func is not None:# ...# endpoint为key将对应的view_func保存在 view_functions属性中# view_functions 是个 dictself.view_functions[endpoint] = view_func

很简单,add_url_rule 主要作用就是 将rule和view_func 信息维护到 flask实例的 url_map和view_functions属性中

ok,继续研究 app.run() 启动流程就在其中(app.run()会启动一个简易的web服务器用于开发和测试,生产环境会用其他高性能的web server,不过借助这个研究下启动流程还是可以的)

# flask/app.py L889
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):# ...from werkzeug.serving import run_simpletry:# 借助了 werkzeug.serving.run_simple# 注意第三个参数 将flask实例传给了 run_simple方法# 后续 web server会调用flask实例进行逻辑处理run_simple(host, port, self, **options)finally:# reset the first request information if the development server# reset normally.  This makes it possible to restart the server# without reloader and that stuff from an interactive shell.self._got_first_request = False

接着看 run_simple代码,这边切换为研究 werkzeug 源码

# werkzeug/serving.py L876
def run_simple(hostname,port,application,use_reloader=False,use_debugger=False,use_evalex=True,extra_files=None,reloader_interval=1,reloader_type="auto",threaded=False,processes=1,request_handler=None,static_files=None,passthrough_errors=False,ssl_context=None,
):#...# application 参数对应的是 flask实例def inner():try:fd = int(os.environ["WERKZEUG_SERVER_FD"])except (LookupError, ValueError):fd = None# 关键于此 创建一个server实例srv = make_server(hostname,port,application,threaded,processes,request_handler,passthrough_errors,ssl_context,fd=fd,)if fd is None:log_startup(srv.socket)# 调用web server实例的serve_forever方法srv.serve_forever()if use_reloader:#...else:inner()

继续往下,先看 make_server方法

# werkzeug/serving.py L830
def make_server(host=None,port=None,app=None,threaded=False,processes=1,request_handler=None,passthrough_errors=False,ssl_context=None,fd=None,
):if threaded and processes > 1:raise ValueError("cannot have a multithreaded and multi process server.")elif threaded:return ThreadedWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd)elif processes > 1:return ForkingWSGIServer(host,port,app,processes,request_handler,passthrough_errors,ssl_context,fd=fd,)else:return BaseWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd)

看出来就是根据具体参数 构建一个server实例返回 挑一个看下 看 BaseWSGIServer

# werkzeug/serving.py L708
class BaseWSGIServer(HTTPServer, object):def __init__(self,host,port,app,handler=None,passthrough_errors=False,ssl_context=None,fd=None,):if handler is None:# 此处注意下 后续的请求具体处理会走 WSGIRequestHandlerhandler = WSGIRequestHandler# 这边进行了 端口绑定和监听# 并且传入了类 WSGIRequestHandlerHTTPServer.__init__(self, server_address, handler)# app即flaks实例self.app = app# 此方法在make_server 返回实例后立马调用def serve_forever(self):self.shutdown_signal = Falsetry:# 此处的 HTTPServer 位于python内置库http中 http.server.HTTPServer# 将当前server实例作为参数传入HTTPServer.serve_forever(self)except KeyboardInterrupt:passfinally:self.server_close()

在往下看 HTTPServer.serve_forever() 可以发现 serve_forever 其实是其父类的父类 内置库 socketserver中 BaseServer 类实现的方法

# socketserver.py L153
class BaseServer:timeout = Nonedef __init__(self, server_address, RequestHandlerClass):"""Constructor.  May be extended, do not override."""self.server_address = server_address# 这个属性就是 WSGIRequestHandlerself.RequestHandlerClass = RequestHandlerClassself.__is_shut_down = threading.Event()self.__shutdown_request = Falsedef serve_forever(self, poll_interval=0.5):self.__is_shut_down.clear()try:with _ServerSelector() as selector:selector.register(self, selectors.EVENT_READ)# 就是个无限循环 接收一个请求处理一个请求while not self.__shutdown_request:ready = selector.select(poll_interval)# bpo-35017: shutdown() called during select(), exit immediately.if self.__shutdown_request:breakif ready:# 接收到具体请求后 进行处理self._handle_request_noblock()self.service_actions()finally:self.__shutdown_request = Falseself.__is_shut_down.set()def _handle_request_noblock(self):try:request, client_address = self.get_request()except OSError:returnif self.verify_request(request, client_address):try:# 接收到具体请求后 进行处理self.process_request(request, client_address)except Exception:self.handle_error(request, client_address)self.shutdown_request(request)except:self.shutdown_request(request)raiseelse:self.shutdown_request(request)def process_request(self, request, client_address):# 接收到具体请求后 进行处理self.finish_request(request, client_address)self.shutdown_request(request)def finish_request(self, request, client_address):"""Finish one request by instantiating RequestHandlerClass."""# 每次都实例化了 WSGIRequestHandler 进行请求处理# 注意哦 第三个参数把当前的server实例传入self.RequestHandlerClass(request, client_address, self)

好的 最终最终 请求的处理是在 WSGIRequestHandler 实例化过程中处理的
那我们来看看 WSGIRequestHandler的代码吧
WSGIRequestHandler 的初始化其实走父类的父类 BaseRequestHandler 的__init__方法

# socketserver.py 696
class BaseRequestHandler:def __init__(self, request, client_address, server):self.request = requestself.client_address = client_addressself.server = serverself.setup()try:# 具体的处理方法 其实是调用的子类的实现self.handle()finally:self.finish()def setup(self):passdef handle(self):passdef finish(self):pass# werkzeug/serving.py  L163
class WSGIRequestHandler(BaseHTTPRequestHandler, object):# 处理请求的核心方法def run_wsgi(self):# 此处经典的 将http内容转化为符合wsgi格式的内容 因为后面是个wsgi的app啊self.environ = environ = self.make_environ()headers_set = []headers_sent = []# 眼熟吧 调用wsgi app的参数之一def start_response(status, response_headers, exc_info=None):if exc_info:try:if headers_sent:reraise(*exc_info)finally:exc_info = Noneelif headers_set:raise AssertionError("Headers already set")headers_set[:] = [status, response_headers]return writedef execute(app):# 经典的wsgi app调用 返回值可迭代# 此处的app是flask实例 实例可调用自然是因为Flask实现了__call__方法application_iter = app(environ, start_response)try:# 执行此方法 参数为flask实例execute(self.server.app)except (_ConnectionError, socket.timeout) as e:self.connection_dropped(e, environ)except Exception:#...# 上一个类调用的handle就是这个def handle(self):"""Handles a request ignoring dropped connections."""try:# 调用父类的handleBaseHTTPRequestHandler.handle(self)except (_ConnectionError, socket.timeout) as e:self.connection_dropped(e)except Exception as e:if self.server.ssl_context is None or not is_ssl_error(e):raiseif self.server.shutdown_signal:self.initiate_shutdown()# 没错 下面的类又调用了这个方法def handle_one_request(self):"""Handle a single HTTP request."""self.raw_requestline = self.rfile.readline()if not self.raw_requestline:self.close_connection = 1elif self.parse_request():# 最终最终 走了这个方法return self.run_wsgi()# http/server.py 147
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):# 上一个类调用的就是这个def handle(self):"""Handle multiple requests if necessary."""self.close_connection = True# 此处又调用回了 WSGIRequestHandler 的 handle_one_requestself.handle_one_request()while not self.close_connection:self.handle_one_request()

上面的调用路线看起来稍微有点绕,是因为涉及到了继承方法的覆盖,有些调用走的父类的,有些走的子类的方法。

可以看到:其实就是监听端口,并开启一个无限的循环,每次接收到一个请求之后,就实例化WSGIRequestHandler进行处理,而WSGIRequestHandler主要做了HTTP格式数据到WSGI数据的转换,然后用WSGI的方式调用了flask实例进行实际的逻辑处理并返回数据。

下面来看看Flask实例支持调用的代码吧。

# flask/app.py L103
class Flask(_PackageBoundObject):def __call__(self, environ, start_response):return self.wsgi_app(environ, start_response)def wsgi_app(self, environ, start_response):# 重要 创建当前请求的上下文对象 flask.ctx.RequestContext对象# 此处已经对请求信息进行了处理(获得了 endpoint和view_func_args)ctx = self.request_context(environ)error = Nonetry:try:# 将当前请求的上下文入栈(LocalStack)# 此处是Flask上下文的实现细节 通过共享的方式来传递上下文 给后面的view_funcctx.push()# 分发执行response = self.full_dispatch_request()except Exception as e:error = eresponse = self.handle_exception(e)except:  # noqa: B001error = sys.exc_info()[1]raisereturn response(environ, start_response)finally:if self.should_ignore_error(error):error = None# 请求处理完成后 清理上下文 出栈ctx.auto_pop(error)def full_dispatch_request(self):# hookself.try_trigger_before_first_request_functions()try:# hookrequest_started.send(self)rv = self.preprocess_request()if rv is None:# 主要执行逻辑的方法rv = self.dispatch_request()except Exception as e:rv = self.handle_user_exception(e)return self.finalize_request(rv)def dispatch_request(self):# 直接从上下文栈中获取当前的请求上下文req = _request_ctx_stack.top.requestif req.routing_exception is not None:self.raise_routing_exception(req)rule = req.url_rule# ...# 根据endpoint获取对应的view_func 执行并返回return self.view_functions[rule.endpoint](**req.view_args)

okk,到此处Flask启动流程基本就差不多了。
总结一下步骤:

  1. 创建一个Flask实例 app
  2. 将url和view_func 通过 app.add_url_rule() 维护到app的url_map和view_functions 属性中。url_map包含了路由逻辑,view_functions存储了对应的逻辑函数,二者通过endpoint相关联。
  3. 步骤1和2 完成后,其实一个遵循WSGI协议的 web application已经准备好了,接下来我们要做的就是将其挂到一个同样支持WSGI协议的web server下面。web server接受HTTP协议的请求,并将其转化为WSGI格式的内容,然后调用 app(environ, start_response) 执行具体逻辑处理,并返回WSGI格式的结果。之后再把WSGI格式的结果转换为HTTP格式返回给客户端就可以啦。
  4. werkzeug 中的 BaseWSGIServer 继承了Python内置库的 http.server.HTTPServer,从中获得了HTTPServer监听端口并获取请求的能力,并整合了 app和 WSGIRequestHandler。每当一个请求就绪时,就交给一个WSGIRequestHandler实例处理。WSGIRequestHandler做了HTTP格式数据到WSGI格式的转换,并执行app(environ, start_response) ,返回响应。
  5. app(environ, start_response) 这步就又回到flask的请求处理逻辑,根据environ的信息配合事先已经绑定好的 url_map,得到具体的路由信息和参数(endpoint,view_func_args),然后从 view_functions 字典中取出对应的view_function运行并返回结果。

Flask1.1.4 Werkzeug1.0.1 源码分析:启动流程相关推荐

  1. Flask1.1.4 Werkzeug1.0.1 源码分析:路由

    路由大致分为两部分: 路由绑定:将url_rule和view_func 绑定到falsk实例中,这个在之前的一篇 启动流程中已经讲过了 路由解析:当实际请求来时,根据environ去匹配到对应的Rul ...

  2. uboot 2021.10源码分析(启动流程)

    uboot版本:2021.10 平台:armv8  rk3399  eMMC 16G  LPDDR4 4G 本文主要基于uboot的执行流程进行分析而忽略了相关细节,从uboot的基本框架结构着手,新 ...

  3. Spark2.4.0 SparkEnv 源码分析

    Spark2.4.0 SparkEnv 源码分析 更多资源 github: https://github.com/opensourceteams/spark-scala-maven-2.4.0 时序图 ...

  4. 菜鸟读jQuery 2.0.3 源码分析系列(1)

    原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...

  5. Android 11.0 Settings源码分析 - 主界面加载

    Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...

  6. Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析

    相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...

  7. photoshop-v.1.0.1源码分析第三篇–FilterInterface.p

    photoshop-v.1.0.1源码分析第三篇–FilterInterface.p 总体预览 一.源码预览 二.语法解释 三.结构预览 四:语句分析 五:思维导图 六:疑留问题 一.源码预览 {Ph ...

  8. Spark2.0.2源码分析——RPC 通信机制(消息处理)

    RPC 是一种远程过程的调用,即两台节点之间的数据传输. 每个组件都有它自己的执行环境,RPC 的执行环境就是 RPCENV,RPCENV 是 Spark 2.x.x 新增加的,用于替代之前版本的 a ...

  9. Pushlet 2.0.3 源码分析

    转载地址:http://blog.csdn.net/yxw246/article/details/2418255 Pushlet 2.0.3 源码分析 ----服务器端 1 总体架构 Pushlet从 ...

最新文章

  1. 面向软件工程师的卡尔曼滤波器
  2. 不平等加剧与问责机制:纽约大学AI Now研究所2018 AI现状报告 | 报告
  3. 话里话外:浅谈国企绩效考核问题(二)
  4. Matplotlib库入门
  5. DIY斑竹管理初稿的确定
  6. 实验2-1-3 计算物体自由下落的距离 (5 分)
  7. linux 获取网站预览图,Shell脚本实现获取网页快照并生成缩略图 -电脑资料
  8. 阿里云windows 2012服务器部署java web程序教程
  9. jsp分页代码mysql_jsp分页(jsp分页完整代码)
  10. PotPlayer LAV MadVR SVP4/BFRC播放器设置(极为详细,硬解,渲染,插帧按CPU性能评级)
  11. 凯利公式计算器安卓_华为MatePad Pro 5G评测:一屏双任务,打破安卓平板生态限制...
  12. 长期听耳机有什么坏处?哪种类型的耳机对耳朵危害小?
  13. 简单Beautiful Soup教程
  14. 深信服技术认证之容灾与备份(一)
  15. 布兰迪斯大学计算机科学专业,美国布兰迪斯大学计算机科学博士专业介绍
  16. sqlserver 取余和取整
  17. putty 下载地址
  18. PBRT_V2 总结记录 81 Importance Sampling
  19. sync、fsync、fdatasync、fflush函数区别和使用举例
  20. java将中文转为拼音

热门文章

  1. 算法工程-Faiss原理和使用总结
  2. 宜人贷蜂巢API网关技术解密之Netty使用实践 1
  3. Linux命令行安卓模拟器,linux下安卓模拟器加速
  4. 90后程序员已经出家了
  5. CAD标注:折弯标注的使用技巧
  6. 【深度学习论文翻译】应用于语义分割问题的深度学习技术综述01
  7. js实现图片循环播放--带按钮功能(广告轮播效果)~~~(超简单代码实现)
  8. css vue 内联_04-Vue基础-css和内联样式绑定
  9. Redis原理剖析视频
  10. nodejs+vue+elementui游戏新闻资讯网站管理系统