基础概念

首先要了解 WSGI 规范的概念,WSGI(Web Server Gateway Interface)规范描述了web server(Gunicorn,uWSGI等)如何与web application(flask, django等)交互、web application如何处理请求,定义在 pep 3333。正是有了 WSGI 规范,我们才能在任意 web server 上跑各种 web 应用。WSGI API 定义看起来很简单:

def application(environ, start_response)
  • application 就是 WSGI app,一个可调用对象

  • 参数:

    • environ: 一个包含 WSGI 环境信息的字典,由 WSGI 服务器提供,常见的 key 有 PATH_INFO,QUERY_STRING 等
    • start_response: 生成 WSGI 响应的回调函数,接收两个参数,status 和 headers
  • 函数返回值为响应体的迭代器 ###简单举例 下面举个简单的例子,比如一个返回 hello world 的应用:

def application(environ, start_response):
status = '200 OK'
headers = [('Content-Type', 'text/html; charset=utf8')]
start_response(status, headers)
return [b"<h1>Hello, World!</h1>"]
 

werkzeug相关

werkzeug是Python实现的WSGI规范的使用函数库。 正如werkzeug官网Werkzeug上所说,werkzeug使用起来非常简单,但是却非常强大。关于使用简单的这个特性,官网给了一段示例代码。

from werkzeug.wrappers import Request, Response
@Request.application
def application(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', , application)

###简单小结 关于上面的代码我做一下总结: application--可调用对象,wsig模块中加括号括号执行 application的返回值--Response对象,wsgi中会对该对象加括号执行其__call__方法 一次成功的访问,由以下几步完成

  1. 浏览器(client)发送一个请求(request
  2. 服务器(server)接收到请求
  3. 服务器处理请求
  4. 返回处理的结果(response
  5. 浏览器处理返回的结果,显示出来。

Detail

具体来说:

  1. wigi相关模块通过建立socket拿到客户端发送的数据,然后进行解析,然后封装到environ中
  2. web框架比如flask,他拿到environ,执行其内部各种调用函数,视图函数,然后返回Response对象
  3. wigi相关模块拿到相应的Response对象,执行其__call__方法拿到app_iter对象,进行for循环进行socket.sendall(data)方法进行数据发送 ###源码 现在我们开始看一下源码:
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 log_startup(sock):
display_hostname = hostname not in ('', '*') and hostname or 'localhost'
if ':' in display_hostname:
display_hostname = '[%s]' % display_hostname
quit_msg = '(Press CTRL+C to quit)'
port = sock.getsockname()[1]
_log('info', ' * Running on %s://%s:%d/ %s',
ssl_context is None and 'http' or 'https',
display_hostname, port, quit_msg) 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() inner()
 

执行inner方法 然后执行make_server方法拿到其返回值并赋值给srv

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类为例,将其实例化就是执行其__init__方法 因为类的各种继承,我就不一一细说了: 总的来说:

就是创建socket和定义处理request的类RequestHandleClass
其为:WSGIRequestHandler

然后执行srv.server_forver srvBaseWSGIServer的实例,根据类的继承,去查找各种方法. 记住一点就是查找方法优先从自己的类定义中找,如果没有就去父类中找.时刻谨记self是谁

#BaseWSGIServer中定义
def serve_forever(self):
self.shutdown_signal = False
try:
HTTPServer.serve_forever(self)
except KeyboardInterrupt:
pass
finally:
self.server_close()
 
###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()
# BaseServer
def _handle_request_noblock(self):
"""Handle one request, without blocking. I assume that selector.select() has returned that the socket is
readable before this function was called, so there should be no risk of
blocking in get_request().
"""
try:
request, client_address = self.get_request()
except OSError:
return
if 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)
raise
else:
self.shutdown_request(request)
 

执行process_request方法

 def process_request(self, request, client_address):
"""Call finish_request.
Overridden by ForkingMixIn and ThreadingMixIn.
"""
self.finish_request(request, client_address)
self.shutdown_request(request)
 

Next

def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
 

执行RequestHandlerClass类的实例化

执行BaseHTTPRequestHandler的handle方法 WSGIRequestHandler.handle_one_request

def handle_one_request(self):
"""Handle a single HTTP request."""
self.raw_requestline = self.rfile.readline()
if not self.raw_requestline:
self.close_connection = 1
elif self.parse_request():
return self.run_wsgi()
 def run_wsgi(self):
if self.headers.get('Expect', '').lower().strip() == '100-continue':
self.wfile.write(b'HTTP/1.1 100 Continue\r\n\r\n') self.environ = environ = self.make_environ()
headers_set = []
headers_sent = [] def write(data):
assert headers_set, 'write() before start_response'
if not headers_sent:
status, response_headers = headers_sent[:] = headers_set
try:
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 = True
self.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() def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if headers_sent:
reraise(*exc_info)
finally:
exc_info = None
elif headers_set:
raise AssertionError('Headers already set')
headers_set[:] = [status, response_headers]
return write def execute(app): # app_iter对象 包含了需要返回的各项数据
application_iter = app(environ, start_response) # Flask实例的call方法返回的的response对象的__call__方法返回的东西
try:
for data in application_iter:
write(data)
if not headers_sent:
write(b'')
finally:
if hasattr(application_iter, 'close'):
application_iter.close()
application_iter = None try:
execute(self.server.app)
except (socket.error, socket.timeout) as e:
self.connection_dropped(e, environ)
except Exception:
if self.server.passthrough_errors:
raise
from werkzeug.debug.tbtools import get_current_traceback
traceback = get_current_traceback(ignore_system_exceptions=True)
try:
# if we haven't yet sent the headers but they are set
# we roll back to be able to set them again.
if not headers_sent:
del headers_set[:]
execute(InternalServerError())
except Exception:
pass
self.server.log('error', 'Error on request:\n%s',
traceback.plaintext)

通过这个代码,我们拿到了app执行后拿到的可迭代对象 application_iter = app(environ, start_response) # Flask实例的call方法返回的的response对象的__call__方法返回的可迭代对象

END

最终for循环这个对象发送了数据

for data in application_iter:
write(data)
转载https://juejin.im/post/5c66be3f6fb9a049dd80d2f2

关于wsgi协议的理解的更多相关文章

  1. wsgi协议

    用来为server程序和app/framework程序做连接桥梁的,使server和app/framework各自发展,任意组合 上图是python3.4标准库里面,关于wsgiserver的实现.从 ...

  2. Python Web开发中,WSGI协议的作用和实现原理详解

    首先理解下面三个概念: WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server ...

  3. Python Web开发中的WSGI协议简介

    在Python Web开发中,我们一般使用Flask.Django等web框架来开发应用程序,生产环境中将应用部署到Apache.Nginx等web服务器时,还需要uWSGI或者Gunicorn.一个 ...

  4. Python web框架开发 - WSGI协议

    浏览器进行http请求的时候,不单单会请求静态资源,还可能需要请求动态页面. 那么什么是静态资源,什么是动态页面呢? 静态资源 : 例如html文件.图片文件.css.js文件等,都可以算是静态资源 ...

  5. HTTPS强制安全策略-HSTS协议阅读理解

    https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security [阅读理解式翻译,非严格遵循原 ...

  6. python - wsgi协议

    wsgi - python web server gateway interface 出现的目的是,为了在 python框架开发的时候,更具有通用性.只要符合 wsgi标准,就可以自由选择服务器(ng ...

  7. WSGI协议以及对服务器的影响

    下面的内容纯属个人学习心得,如果对于我的观点有疑问,敬请留言,我将虚心向大牛学习. WSGI的全称是WEB SERVICE GATEWAY INTERFACE.WSGI 不是服务器,不是API,也不是 ...

  8. wsgi 协议

    wsgi 协议 前言 本来没打算这么早就学习 wsgi 的,因为想要学习python 是如何处理网络请求的绕不开 wsgi,所以只好先学习一下 wsgi.先对 wsgi 有个印象,到了学习 Djang ...

  9. WSGI协议解析

    WSGI协议中包含两个角色:服务器方和应用程序: 服务器方:其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递 ...

随机推荐

  1. 还原Azure DevOps Server (TFS)中误删除的生成流水线

    流水线历史记录 DevOps Server流水线的历史记录有完善的版本日志,用户可以随时回退到修改过程中的任何一个版本,还能比较差异.这个历史记录功能可以和代码库中的版本控制媲美. 图一:生成历史记录 ...

  2. 关于调试WCF时引发的异常XmlException: Name cannot begin with the '<' character, hexadecimal value 0x3C” on Client Side

    问题描述:在使用VS2015调试WCF时,偶遇抛出异常名称不能以“<”字符(十六进制0x3c)开头,平时运行时(不调试)没有问题的. 解决方法:检查后发现为了检查异常的位置,勾选了引发通用语言运 ...

  3. Lerning Entity Framework 6 ------ Handling concurrency With SQL Server Database

    The default Way to handle concurrency of Entity Framework is using optimistic concurrency. When two ...

  4. setAttribute的浏览器兼容性(转)

    1.element要用getElementById or ByTagName来得到, 2.setAttribute("class", vName)中class是指改变"c ...

  5. WebMvcConfigurer

    [传送门]:详解WebMvcConfigurer接口 1. 设置跨域规则 @Configuration public class CrossOriginConfig implements WebMvc ...

  6. 【xsy1596】旅行 期望+状压DP

    题目大意:有$m$个人要从城市$1$开始,依次游览城市$1$到$n$. 每一天,每一个游客有$p_i$的概率去下一个城市,和$1-p_i$的概率结束游览. 当游客到达城市$j$,他会得到$(1+\fr ...

  7. Django--模板层template

    一 模版简介 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. def current_datetime(request): now ...

  8. xshell 会话管理器快捷键

    有没有发现xshell6关闭左边的会话管理器以后,打开就比较麻烦 那么可以自定义一个快捷键来打开: 然后输入一个快捷键 类型选择 菜单-->然后找会话管理器 完事儿 也可以自定义其他快捷键.自己 ...

  9. 通过清华大学镜像下载Android源码并编译源码

    之前看源码都是在Windows下用SourceInsight看,虽然达到了研究源码的效果,但终究还是有遗憾...趁着周末,准备在Ubuntu虚拟机上下载编译源码. 之前下源码时,有了解一些Androi ...

  10. 【Java基本功】聊聊抽象类和接口的区别

    1 抽象类一般会实现一部分操作,并且留一些抽象方法让子类自己实现,比如Stringbuffer和Stringbuilder的父类abstractStringbuilder. 2 接口一般指一种规定,比 ...