本文主要分享python当前最流行的网络开发框架flask有关服务器启动的原理,并介绍了相关的web框架的底层支持和相关的术语
![2381f5ca600efdc345392f7a95c63fd3.png](https://i-blog.csdnimg.cn/blog_migrate/9f19751e8be9dc9b8a2f90ca1f9a08d4.jpeg)
一些概念
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 使用的比较多的服务器主要有Gunicorn,Waitress,uWSGI,配合这些服务器使用的一般是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网站保持友好的沟通……,时不时的会分享一些提高效率的编程小技巧,在实际应用中遇到的问题以及解决方案,或者源码的阅读等等,欢迎大家一起来讨论!如果觉得写的还不错,欢迎点个关注点个赞,谢谢。