Flask werkzeug 源码解析

Flask werkzeug流程大概:执行run_simple ,实际执行为先用make_server 创建一个 BaseServer 实例,然后执行 实例的serve_forever 方法, serve_forever 调用  run_simple 传入的第三个参数,执行(self, environ, start_response) ,environ 为 初步处理的request 请求,start_response 为回调函数;

若第三个参数为   Response 的实例化对象,以下称obj,obj 是具体的request 入口,负责具体的逻辑,其他不同的框架其实是第三个参数不同,Flask由此而来。Flask 的三种方式

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

# 方式一:实例化Response,最后 self.request = encode('Hello World application1!')
application1 = Response('Hello World application1!')


#方式二
def application2(environ, start_response):
    request = Request(environ)
    response = Response("Hello %s!" % request.args.get('name', 'World!'))
    return response(environ, start_response)


#方式三
@Request.application
def hello(request):
    return Response('Hello World Request!')

if __name__ == '__main__':
    # run_simple('localhost', 4000, application1)
    # run_simple('localhost', 4000, application2)
    run_simple('localhost', 4000, hello)

我们在浏览器输入http://localhost:4000/就会得到response信息 
接下来我们就简单的分析下,该模块的请求、响应流程

werkzeug包下的__init__.py模块初始化时,遍历循环字典all_by_module,重新构造object_origins字典数据格式,该字典类型格式如下,我列举出来一些元素,以下是键值对形式

# BaseResponse - --- werkzeug.wrappers
# BaseRequest - --- werkzeug.wrappers
# Request - --- werkzeug.wrappers
# Response - --- werkzeug.wrappers

该字典的键是werkzeug下的某模块中的函数、方法,值是werkzeug下的某模块中。我们回头看我们的demo示例,在文件起始处我们引入了from werkzeug.serving import run_simple。我们跟踪代码去看下serving.py模块下的run_simple函数

####################### serving.py 文件下的 run_simple 函数 ############################
#  run_simple最后执行inner函数
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)

# 参数
hostname:应用程序的主机
port:端口
application:WSGI应用程序
use_reloader:如果程序代码修改,是否需要自动启动服务
use_debugger:程序是否要使用工具和调试系统
use_evalex:应用是否开启异常评估
extra_files:重载器应该查看的文件列表附加到模块。例如配置文件夹
reloader_interval:秒重载器的间隔
reloader_type:重载器的类型
threaded:进程是否处理单线程的每次请求
processes:如果大于1,则在新进程中处理每个请求。达到这个最大并发进程数
request_handler:可以自定义替换BaseHTTPRequestHandler
static_files:静态文件路径的列表或DICT
passthrough_errors:将此设置为“真”以禁用错误捕获。这意味着服务器会因错误而死亡
ssl_context:如何进行传输数据加密,可以设置的环境


# run_simple函数中,最后会执行到
if use_reloader:
    # 省略,太长了,反正暂时也执行不到
else:
    inner()

################ serving.py 文件下的 run_simple 函数下的 inner()函数 #####################
def inner():
    try:
        fd = int(os.environ['WERKZEUG_SERVER_FD'])
    except (LookupError, ValueError):
        fd = None
    # 通过make_server方法,跟进我们在初始化__init__中的参数,去构造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)
    # 把服务运行起来 serve_forever() 是HTTPserver的方法,
    # 当有请求过来之后,server_forever会将run_simple()中的第三个参数加括号执行,并传入参数(environ, start_response)

    srv.serve_forever()
############### BaseWSGIServer 类下的 serve_forever 方法 #######################
class BaseWSGIServer(HTTPServer, object)
    def serve_forever(self):
        self.shutdown_signal = False
        try:
            # class HTTPServer(socketserver.TCPServer)未实现 serve_forever ————> 
            # class TCPServer(BaseServer) 未实现 serve_forever :
            # BaseServer 实现 serve_forever
            HTTPServer.serve_forever(self)
        except KeyboardInterrupt:
            pass
        finally:
            self.server_close()
################ serving.py 文件下的 def make_server  #####################
def make_server(host=None, port=None, app=None, threaded=False, processes=1,request_handler=None,
                passthrough_errors=False,ssl_context=None, fd=None):

    """Create a new server instance that is either threaded, or forks
    or just processes one request after another."""

    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:
        # 没想到吧,最后执行这个 ,实例化BaseWSGIServer
        return BaseWSGIServer(host, port, app, request_handler,passthrough_errors, ssl_context, fd=fd)
########## socketserver.py 文件下的 class BaseServer 下的 serve_forever #######
# 具体的监听 socket ,当有请求到来时,执行传入的第三个 参数,执行格式为 函数(self, environ, start_response),并接受返回值
class BaseServer:
    def serve_forever(self, poll_interval=0.5):

        """Handle one request at a time until shutdown.
    
        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            # XXX: Consider using another file descriptor or connecting to the
            # socket to wake this up instead of polling. Polling reduces our
            # responsiveness to a shutdown request and wastes cpu at all other
            # times.
            with _ServerSelector() as selector:   # 具体监听过程
                selector.register(self, selectors.EVENT_READ)

                while not self.__shutdown_request: 
                    ready = selector.select(poll_interval)
                    if ready:
                        self._handle_request_noblock()

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

我们看下我们的示例中,最简单那个例子application1 = Response('Hello World application1!'),设置run_simple('localhost', 4000, application1),当接受请求时,为什么会执行application1的对象内方法,并且返回给浏览器

在我们示例代码中,当run_simple('localhost', 4000, application1)执行后,实际执行inner(),而inner做了两件事,一件make_server,一件server_forver(一直在监听)。当有请求过来之后,server_forever会将run_simple()中的第三个参数加括号执行,并传入参数(environ, start_response)。所以当http://localhost:4000/请求时,就会触发并调用application1(),即application1 = Response('Hello World application1!').__call(self, environ, start_response)__

在所有的 python web 框架都要遵循 WSGI 协议,WSGI 中有一个非常重要的概念:

每个 python web 应用都是一个可调用(callable)的对象(如上述的Response),要运行 web 应用,必须有 web server,在werkzeug中提供了 WSGIServer,Server 和 Application 之间怎么通信,就是 WSGI 的功能

wsgi有两方,服务器方 和 应用程序

  1. 服务器方:调用应用程序,给应用程序传递(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.
  2. 应用程序:用来生成返回的header,body和status,以便返回给服务器方。

看完了请求,接下来看下返回。即werkzeug.wrappers.py模块下的Response类

################### response.py 文件下的 class Response ################
class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,CommonResponseDescriptorsMixin,WWWAuthenticateMixin):
# 就这么多,没了,气不气

该类是多重继承类,这里主要看下BaseResponse,先看下初始方法

#################### base_responce.py 文件下的 class BaseResponse(object) #############
class BaseResponse(object)def __init__(self, response=None, status=None, headers=None,mimetype=None, content_type=None, direct_passthrough=False):if isinstance(headers, Headers):self.headers = headerselif not headers:self.headers = Headers()else:self.headers = Headers(headers)if content_type is None:if mimetype is None and 'content-type' not in self.headers:mimetype = self.default_mimetypeif mimetype is not None:mimetype = get_content_type(mimetype, self.charset)content_type = mimetypeif content_type is not None:self.headers['Content-Type'] = content_typeif status is None:status = self.default_statusif isinstance(status, integer_types):self.status_code = statuselse:self.status = statusself.direct_passthrough = direct_passthroughself._on_close = []# we set the response after the headers so that if a class changes# the charset attribute, the data is set in the correct charset.if response is None:self.response = []elif isinstance(response, (text_type, bytes, bytearray)):self.set_data(response)   #################看这里else:self.response = response

在BaseResponse类__init__初始方法中,我们定义了返回的Headers、content_type、状态码status,最后通过self.set_data(response),跟踪代码如下:

####### base_responce.py 文件下的 class BaseResponse(object) 下的 def set_data ##########
## 主要讲传入的应答 编码
class BaseResponse(object)def set_data(self, value):if isinstance(value, text_type):# 字符串编码value = value.encode(self.charset)else:value = bytes(value)self.response = [value]  # 看这里,看这里if self.automatically_set_content_length:self.headers['Content-Length'] = str(len(value))

将我们示例中的application1 = Response('Hello World application1!')参数字符串,进行bytes类型转换并赋值给self.response,然后执行对象(),即调用__call__方法,

####### base_responce.py 文件下的 class BaseResponse(object) 下的 def __call__ ##########
# 这个方法的作用就是,执行 具体的请求过程,然后调用回调函数,并提供返回值给调用者 HTTPServer.serve_forever(self)
class BaseResponse(object)def __call__(self, environ, start_response):print(start_response)# get_wsgi_response ,是具体的请求处理过程,后面Flask源码解析会讲到app_iter, status, headers = self.get_wsgi_response(environ)# start_response ,提供的回调函数start_response(status, headers)return app_iter  ### 把值返回个调用者

这里要先介绍一个environ参数,以上方式2中涉及到了environ,其实这个environ参数是包含了请求的所有信息,让我们在看下__call__方法中, app_iter, status, headers = self.get_wsgi_response(environ)输出通过请求系列参数,获取最后要返回的get_wsgi_response,输出如下:

<werkzeug.wsgi.ClosingIterator object at 0x0589C0B0> --- 200 OK --- [('Content-Type'\\\省略]

然后在start_response(status, headers)代码中,start_response 是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息,让我们来看下start_response输出,

<function WSGIRequestHandler.run_wsgi.<locals>.start_response at 0x05A32108>

跟踪代码如下start_response:

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 write

start_response返回write方法,然后跟踪该方法

def write(data):assert headers_set, 'write() before start_response'if not headers_sent:status, response_headers = headers_sent[:] = headers_settry:code, msg = status.split(None, 1)except ValueError:code, msg = status, ""code = int(code)self.send_response(code, msg)header_keys = set()for key, value in response_headers:self.send_header(key, value)key = key.lower()header_keys.add(key)if not ('content-length' in header_keys or environ['REQUEST_METHOD'] == 'HEAD' or code < 200 or code in (204, 304)):self.close_connection = Trueself.send_header('Connection', 'close')if 'server' not in header_keys:self.send_header('Server', self.version_string())if 'date' not in header_keys:self.send_header('Date', self.date_time_string())self.end_headers()assert isinstance(data, bytes), 'applications must write bytes'self.wfile.write(data)self.wfile.flush()

最后就输出到浏览器,以上就是简单的请求、响应流程

Flask示例

我们在Flask中经常会写成

from flask import Flaskapp = Flask(__name__)@app.route('/index')
def index():return 'Hello World'if __name__ == '__main__':app.run() # run_simple(host,port,app)

看一下run 

def run(self, host=None, port=None, debug=None, **options):from werkzeug.serving import run_simpleif host is None:host = '127.0.0.1'if port is None:server_name = self.config['SERVER_NAME']if server_name and ':' in server_name:port = int(server_name.rsplit(':', 1)[1])else:port = 5000if debug is not None:self.debug = bool(debug)options.setdefault('use_reloader', self.debug)options.setdefault('use_debugger', self.debug)try:run_simple(host, port, self, **options)  ## 是不是还是用的run_simplefinally:self._got_first_request = False

最后依然是执行的run_simple(host, port, self, **options),也就是werkzeug.serving.py下的run_simple方法

####################### Flask 的 __call__ 函数 ########################
def __call__(self, environ, start_response):"""The WSGI server calls the Flask application object as theWSGI application. This calls :meth:`wsgi_app` which can bewrapped to applying middleware."""return self.wsgi_app(environ, start_response)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/454475.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AVS 帧内预测模式的汇编优化

王瑞&#xff0a;基金项目&#xff1a;本课题得到国家自然科学基金资助项目基金&#xff08;项目编号&#xff1a;60772101&#xff09;的资助。作者简介&#xff1a;王瑞&#xff08;1986—&#xff09;, 男, 山东莱芜人, 硕士, 主要从事视频压缩方面的研究. E&#xff0d;mai…

ltsc系统激活_WIN10_X64企业版LTSC 电脑公司装机版 202008

文件: WIN10_X64_LTSC_ZJ202008.esd大小: 7431429353 字节(6.92G)MD5: A3A3B15ED47216E177C924D2E07E0799SHA1: 3A647265E0C8234225C633407093BAA07253FB34CRC32: 32E791E9(注意&#xff0c;下载文件有一定几率损坏&#xff0c;如文件值不对请重新下载&#xff01;)360安全云盘…

大学计算机应用基础考试题库,大学计算机应用基础考试题库

综合模拟(四)一、选择题。1、完整的计算机硬件系统一般包括外部设备和 C 。A、运算器的控制器 B、存储器 C、主机 D、中央处理器2、计算机能够自动工作&#xff0c;主要是因为采用了 D 。A、二进制数制 B、高速电子元件 C、存储程序控制 D、程序设计语言3、下面哪能一组是系统软…

Lombok 使用小结

Lombok 使用小结 Lombok 简介Lombok 安装Lombok 使用 API示例示例源码引用和引申Lombok 简介 Lombok 是一种 Java 实用工具&#xff0c;可用来帮助开发人员消除 Java 的冗长&#xff0c;尤其是对于简单的 Java 对象&#xff08;POJO&#xff09;。它通过注释实现这一目的。通过…

html表单input file,input标签type=file的文件上传

一&#xff0c;通过表单提交的方式该提交方式只是提交普通表单&#xff1b;对于file组所选中的文件内容是不上传的&#xff0c;因此需要设置&#xff1a;enctype属性enctype"multipart/form-data"如果想上传多文件&#xff0c;可添加multiple二&#xff0c;通过Ajax异…

AVS游程解码、反扫描、反量化和反变换优化设计

中图分类号:TN919.81   文献标识码:A   文章编号:1009-2552 (2007) 02-0054-04AVS游程解码、反扫描、反量化和反变换优化设计赵 策, 刘佩林(上海交通大学电子工程系, 上海200240)摘 要: 提出了一种适用于AVS的游程解码、反扫描、反量化和反变换硬件结构优化设计方案。根据…

Django REST framework介绍

现在前后端分离的架构设计越来越流行&#xff0c;业界甚至出现了API优先的趋势。 显然API开发已经成为后端程序员的必备技能了&#xff0c;那作为Python程序员特别是把Django作为自己主要的开发框架的程序员&#xff0c;Django REST framework&#xff08;DRF&#xff09;这个…

zabbix 安装_安装zabbix

准备一个纯净环境10.0.0.99首先修改yum源&#xff0c;修改为zabbix清华源&#xff0c;清华源玉zabbix官方源都是同步的&#xff0c;下载速度更快&#xff01;zabbix官方Download Zabbix​www.zabbix.com点击下载&#xff0c;下面有zabbix的历史版本以及官方安装文档可以查看到不…

拓展欧几里得 [Noi2002]Savage

对于一个野人&#xff0c;他&#xff08;她&#xff1f;&#xff09;所在的位置&#xff0c;&#xff08;C[i]x*p[i]&#xff09;%ans,是的&#xff0c;暴力枚举每一个ans&#xff0c;用拓展欧几里得求出每两个wildpeople(wildrage?)相遇的年份&#xff0c;如果小于最小的寿限…

CCNP-19 IS-IS试验2(BSCI)

CCNP-19 IS-IS试验2 实验拓扑&#xff1a;试验要求&#xff1a;R1 R2 R3全部采用集成的ISIS路由协议&#xff0c;R1 R2在区域49.0001内&#xff0c;R3在区域49.0002内&#xff0c;R1与R2之间的链路类型为L1&#xff0c;R2与R3之间的链路类型为L2。 试验目的&#xff1a;掌握基…

正道的光用计算机,正道的光作文500字

当那熟悉的轰天巨雷般的呼噜声响起&#xff0c;我就知道&#xff0c;这又是睡不着的一天。同样在宿舍&#xff1b;同样是小翟&#xff1b;同样的时间&#xff1b;同样在我昏昏欲睡的时候&#xff0c;那个熟悉的呼噜声&#xff0c;它又来了。它将我从即将到来的美梦中惊醒了&…

AVS高清立体视频编码器

一、成果项目背景 电视技术在经历了从黑白到彩色、从模拟到数字的技术变革之后正在酝酿另一场技术革命&#xff0c;从单纯观看二维场景的平面电视跨越到展现三维场景的立体电视。立体电视&#xff0c;又称三维电视(3DTV)&#xff0c;提供了更为丰富的视觉信息和更具临场感的观…

RESTful介绍

RESTful介绍 REST与技术无关&#xff0c;代表的是一种软件架构风格&#xff0c;REST是Representational State Transfer的简称&#xff0c;中文翻译为“表征状态转移”或“表现层状态转化”。阮一峰 理解RESTful架构 RESTful API设计指南 阮一峰 RESTful设计指南 API与用户…

dijkstra算法代码_数据科学家需要知道的5种图算法(附代码)

在本文中&#xff0c;我将讨论一些你应该知道的最重要的图算法&#xff0c;以及如何使用Python实现它们。作者&#xff1a;AI公园导读因为图分析是数据科学家的未来。作为数据科学家&#xff0c;我们对pandas、SQL或任何其他关系数据库非常熟悉。我们习惯于将用户的属性以列的形…

大暴搜 chess

仔细读题&#xff0c;会发现吃掉敌人点对方案数的贡献很神奇。如果走的空格相同&#xff0c;而走的敌人点不同&#xff0c;对答案无贡献&#xff0c;而对于走的空格相同&#xff0c;但一种走了敌人点&#xff0c;另一种没走&#xff0c;算两个方案。。。。sb出题人语文简直是和…

网站的SEO以及它和站长工具的之间秘密

博客迁移没有注意 URL 地址的变化&#xff0c;导致百度和 google 这两只爬虫引擎短时间内找不到路。近段时间研究了下国内最大搜索引擎百度和国际最大搜索引擎google的站长工具&#xff0c;说下感受。 百度的站长工具地址&#xff1a;http://zhanzhang.baidu.com/dashboard/ind…

html 缩略图点击预览,[每天进步一点点~] uni-app 点击图片实现预览图片列表

点击图片&#xff0c;实现预览图片功能&#xff0c;并且可循环预览图片列表&#xff01;image.png一、多张图片预览html代码js代码data(){return {photos:[{ src: 图片路径1},{ src: 图片路径2},{ src: 图片路径3},……]}},methods: {// 预览图片previewImage(index) {let phot…

git ssh拉取代码_阿里云搭建git服务器

一.搭建步骤&#xff0c;分为两步搭建中心仓库自动同步代码到站点目录二.详细步骤如下1.先检查一下服务器上有没有安装gitgit --version如果出现版本号&#xff0c;说明服务器已经安装git&#xff0c;如图所示&#xff1a;2.如果没有版本信息&#xff0c;则先安装git&#xff1…

Django REST framework 序列化

创建一个序列化类 使用序列化有四种方式 使用json模块&#xff0c;完全手写使用django自带的序列化模块 1&#xff0c;# from django.core import serializers 2&#xff0c;# dataserializers.serialize(“json”,book_list)使用REST framework 带的序列化方法&#xff0c…

基于SIMD的AVS整数反变换算法设计与优化

基于SIMD 的AVS 整数反变换算法设计与优化王玲娟&#xff0c;张刚**作者简介&#xff1a;王玲娟&#xff0c;&#xff08;1987-&#xff09;&#xff0c;女&#xff0c;在读硕士&#xff0c;主要研究方向&#xff1a;视频解码算法通信联系人&#xff1a;张刚&#xff0c;&#…