web 服务器 , Web 框架 以及 WSGI
这里说的 Web 服务器特指纯粹的 python HTTP 服务器 (比如 Gunicorn, 而不是 Apache,Nginx 这些)
一般来说, Web 服务器负责建立并管理监听套接字, 并且解析所有收到的 HTTP 请求. Web 框架负责 URL 分发, 根据 HTTP 中请求路径生成响应. 而 WSGI(pep 333) 则是 Web 服务器调用 Web 框架的一个调用惯例. 用来解偶 Web 服务器和 Web 框架.
通过 flask 的源码能比较清晰的看到这三者关系
- def wsgi_app(self, environ, start_response):
- ctx = self.request_context(environ)
- error = None
- try:
- try:
- ctx.push()
- response = self.full_dispatch_request()
- except Exception as e:
- error = e
- response = self.handle_exception(e)
- except:
- error = sys.exc_info()[1]
- raise
- return response(environ, start_response)
- finally:
- if self.should_ignore_error(error):
- error = None
- ctx.auto_pop(error)
- def __call__(self, environ, start_response):
- """The WSGI server calls the Flask application object as the
- WSGI application. This calls :meth:`wsgi_app` which can be
- wrapped to applying middleware."""
- return self.wsgi_app(environ, start_response)
从这里可以看出, flask 的 application 是一个 WSGI 可调用对象. 如果去看 Flask Appication 的 run 方法, 就能发现最终是启动一个 Werkzeug serving.py 中的一个 基于 BaseHTTPServer.HTTPServer 实现的 WSGIServer, 也就是 Web 服务器. 证实 Web 服务器通过 WSGI 接口调用 Web 框架作出响应. 这里也能很清楚的看到 Web 框架的基础功能就是分发请求, 作出响应.
理解全局变量
在 flask 中有 4 个方便好用的全局变量 current_app, request, session, g. 严格意义上来说是线程级别的全局变量 (具体可看 Werkzeug 的 LocalStack 源码), 也就是只有当前线程能访问. 正因为用的多, 更应该花点时间去理解这几个变量的含义以及生命周期.
- # context locals
- _request_ctx_stack = LocalStack()
- _app_ctx_stack = LocalStack()
- current_app = LocalProxy(_find_app)
- request = LocalProxy(partial(_lookup_req_object, 'request'))
- session = LocalProxy(partial(_lookup_req_object, 'session'))
- g = LocalProxy(partial(_lookup_app_object, 'g'))
对于请求 - 响应模式, 响应需要根据具体的请求来做出合理的响应. 这四个全局变量就是用来提供请求的具体信息, 即请求上下文. 从上面 wsgi_app 函数中也可以看出, 有请求过来, 调用 wsig_app 时, 会构建一个当前请求的上下文实例 (RequestContext), 将 http 请求原始信息存到 request 属性, 并将其 push 到当前线程的全局栈 _request_ctx_stack. 接着将当前 flask 实例 push 到 _app_ctx_stack, 存储到上下文实例的 App 属性中. 最后解析请求中的 cookie, 构建 Session 实例存到上下文实例的 session 属性中. 这样每个请求的上下文中就携带了, http 请求的原始信息, 当前 flask 实例上下文信息, 以及 cookie 提取的 session 信息. 一个请求所需要的信息基本就全部包含了. 至于 g 只是当前 flask 实例上下文对象的一个属性. 在响应构造完成后, 会把当前上下文 session 信息存到 cookie, 所以裸 flask 框架的 session 全部信息是存在客户端的 cookie 里面. 接着弹出当前请求的上下文, 弹出当前请求的 flask 实例上下文.
- def push(self):
- """Binds the request context to the current context."""
- top = _request_ctx_stack.top
- if top is not None and top.preserved:
- top.pop(top._preserved_exc)
- # Before we push the request context we have to ensure that there
- # is an application context.
- app_ctx = _app_ctx_stack.top
- if app_ctx is None or app_ctx.App != self.App:
- app_ctx = self.App.app_context()
- app_ctx.push()
- self._implicit_app_ctx_stack.append(app_ctx)
- else:
- self._implicit_app_ctx_stack.append(None)
- if hasattr(sys, 'exc_clear'):
- sys.exc_clear()
- _request_ctx_stack.push(self)
- if self.session is None:
- session_interface = self.App.session_interface
- self.session = session_interface.open_session(
- self.App, self.request
- )
- if self.session is None:
- self.session = session_interface.make_null_session(self.App)
备注:
之前有提到过, Web 服务器与 Web 框架是分开的, 而且 WSGI 也不支持异步服务器. 当 Web 服务器是异步服务器时, 因为 flask 存在线程级全局变量, 且是基于 Werkzeug 中基于 greenlet 的 LocalStack 上实现, 所以当异步服务器不是基于 greenlet 时, 会存在一定问题.
来源: https://www.cnblogs.com/nowg/p/10342534.html