flask request获取raw参数_flask源码阅读系列-flask服务器是怎么跑起来的

本文主要分享python当前最流行的网络开发框架flask有关服务器启动的原理,并介绍了相关的web框架的底层支持和相关的术语

2381f5ca600efdc345392f7a95c63fd3.png

一些概念

flask是什么

官方文档的描述:A microframework based on Werkzeug. It's extensively documented and follows best practice patterns(一个基于'werkzeug'的微型框架,有大量的文档并遵循最佳设计实践)

werkzeug是什么

上面提到,flask是基于werkzeug实现,解释下:Werkzeug是一个实现了WSGI规范的工具包,可以作为python实现Web框架的底层库

WSGI是什么

官方文档:Web Server Gateway Interface,它由Python标准定义的一套Web Server与Web Application的接口交互规范,WSGI不是一个应用、框架、模块或者库,而是规范

  • Web Server:Python 使用的比较多的服务器主要有GunicornWaitressuWSGI,配合这些服务器使用的一般是ngnix(负责负载均衡,存储静态文件等)
  • Web Application:Django,Flask

阅读flask源码之前需要了解的知识点

  • 我们使用的有关web的高级包,基本都是基于socketserver库一层层的封装,默认的符合WSGI规范的服务器类是WSGIServer(基于HTTPServer/TCPServer/BaseServer/),默认的处理器是WSGIRequestHandler(基于BaseHTTPRequestHandler/StreamRequestHandler/BaseRequestHandler)
  • 在启动WSGI服务器的过程中,会设置一些默认env变量,并将符合WSGI规范的处理器和WSGI服务器进行绑定
  • Python满足WSGI标准的程序,一般会以下面这样约定的方式:以一个可调用对象形式来实现
# environ 属性是一个字典,包含了从web服务器如Apache[参考Internet RFC 3875]提供的CGI接口中获取的值# start_response是一个可调用对象,第一个参数是返回的HTTP状态值,第二个参数是(名,值)元组列表,用来构建返回的HTTP头部信息def wsgi_app(environ, start_response):    pass

werkzeug库使用基础

这个werkzeug库会在安装flask的时候自动安装,可以看作flask的组件一样,既然werkzeug实现了WSGI规范,那么只用werkzeug肯定可以直接实现web和客户端的交互,我们看下

from werkzeug.wrappers import Request, Responsedef application(environ, start_response):    # 封装所有传过来的参数    request = Request(environ)    text = 'Hello %s!' % request.args.get('name', 'World')    response = Response(text, mimetype='text/plain')    return response(environ, start_response)if __name__ == '__main__':    from werkzeug.serving import run_simple    run_simple('localhost', 8080, application)

flask开发服务器是怎么跑起来的

体验1分钟之内快速开发一个hello,world应用,看代码

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

运行脚本,打开网页, 访问"http://127.0.0.1:5000/ ",即可看到"hello,world"出现,是不是很简单

源码阅读(基于v0.1)

from flask import Flaskapp = Flask(__name__)if __name__ == '__main__':    app.run()

第一行from flask import Flask,除了标准的导包到当前脚本之外,另外创建了上下文对象current_app,request,session,g,为后面需要上下文环境的时候做准备,如下创建的上下文代码块

# context locals_request_ctx_stack = LocalStack()current_app = LocalProxy(lambda: _request_ctx_stack.top.app)request = LocalProxy(lambda: _request_ctx_stack.top.request)session = LocalProxy(lambda: _request_ctx_stack.top.session)g = LocalProxy(lambda: _request_ctx_stack.top.g)

第二行app = Flask(__name__),创建Flask实例对象,__name__参数表示了Flask实例化所在脚本的脚本名称,用来定位root_path

最后一行app.run()是我们今天主要讲的重点,开启本地开发服务器,对的,就这么一行代码,服务器就启动了,看下源码

def run(self, host='localhost', port=5000, **options):    """Runs the application on a local development server.  If the    :attr:`debug` flag is set the server will automatically reload    for code changes and show a debugger in case an exception happened.    :param host: the hostname to listen on.  set this to ``'0.0.0.0'``                 to have the server available externally as well.    :param port: the port of the webserver    :param options: the options to be forwarded to the underlying                    Werkzeug server.  See :func:`werkzeug.run_simple`                    for more information.    """    from werkzeug import run_simple    if 'debug' in options:        self.debug = options.pop('debug')    options.setdefault('use_reloader', self.debug)    options.setdefault('use_debugger', self.debug)    return run_simple(host, port, self, **options)

我们可以发现,run_simple是核心启动代码,基于werkzeug库,参数分别是(地址,端口号,app, 其他需要的参数),接着看run_simple代码,如下

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,):   """省略了一部分暂时没用的代码,只保留了重点"""    def inner():        try:            fd = int(os.environ["WERKZEUG_SERVER_FD"])        except (LookupError, ValueError):            fd = None        srv = make_server(            hostname,            port,            application,            threaded,            processes,            request_handler,            passthrough_errors,            ssl_context,            fd=fd,        )        if fd is None:            log_startup(srv.socket)        srv.serve_forever()"""省略了一部分暂时没用的代码,只保留了重点"""    else:        inner()

我们发现,核心部分是最后一行的调用,也就是inner函数,看到inner函数内部有一个make_server()方法,基本就能确定肯定是这里实现的WSGI服务器类,因为标准库中wsgiref.simple_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:        return BaseWSGIServer(            host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd        )

看吧,如果只是单线程服务器,那么直接看最后的BaseWSGIServer服务器类就行了,就是在这里初始化WSGISer ver,而这个类就是基于socketserver库类进行一系列的封装,在这个类内部,我们可以看到处理器是WSGIRequestHandler处理器类,这个类也是来自于socketserver库的基础处理器的封装,通过服务器类和特定的处理器类配合使用,再调用底层的标准库方法serve_forever,完美的顺利的启动服务器(和基于socketserver库写一个服务器的启动一模一样,所以你要了解下socketserver的相关知识点,比如下面这样仅仅基于socketserver库启动本地服务器)

# 来自cookbook/python,仅仅基于socketserver实现一个简单收发服务器from socketserver import BaseRequestHandler, TCPServer# 处理器类class EchoHandler(BaseRequestHandler):    # 处理程序    def handle(self):        print('Got connection from', self.client_address)        while True:            msg = self.request.recv(8192)            if not msg:                break            self.request.send(msg)if __name__ == '__main__':    # 服务器启动    serv = TCPServer(('', 20000), EchoHandler)    serv.serve_forever()

总结

本文主要初步了解了Flask框架所涉及到的术语及基本的设计架构

通过阅读源码一步步的了解了Flask的生产服务器启动过程:基于socketserver库设计了符合WSGI规范的服务器类和处理器类,再调用底层的启动服务器函数将服务器启用,等待客户端连接···

我是一名奋战在编程界的pythoner,工作中既要和数据打交道,也要和erp系统,web网站保持友好的沟通……,时不时的会分享一些提高效率的编程小技巧,在实际应用中遇到的问题以及解决方案,或者源码的阅读等等,欢迎大家一起来讨论!如果觉得写的还不错,欢迎点个关注点个赞,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值