关于wsgi协议的理解
基础概念
首先要了解 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__方法 一次成功的访问,由以下几步完成
- 浏览器(client)发送一个请求(request
- 服务器(server)接收到请求
- 服务器处理请求
- 返回处理的结果(response
- 浏览器处理返回的结果,显示出来。
Detail
具体来说:
- wigi相关模块通过建立socket拿到客户端发送的数据,然后进行解析,然后封装到environ中
- web框架比如flask,他拿到environ,执行其内部各种调用函数,视图函数,然后返回Response对象
- 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 srv为BaseWSGIServer的实例,根据类的继承,去查找各种方法. 记住一点就是查找方法优先从自己的类定义中找,如果没有就去父类中找.时刻谨记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协议的理解的更多相关文章
- wsgi协议
用来为server程序和app/framework程序做连接桥梁的,使server和app/framework各自发展,任意组合 上图是python3.4标准库里面,关于wsgiserver的实现.从 ...
- Python Web开发中,WSGI协议的作用和实现原理详解
首先理解下面三个概念: WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server ...
- Python Web开发中的WSGI协议简介
在Python Web开发中,我们一般使用Flask.Django等web框架来开发应用程序,生产环境中将应用部署到Apache.Nginx等web服务器时,还需要uWSGI或者Gunicorn.一个 ...
- Python web框架开发 - WSGI协议
浏览器进行http请求的时候,不单单会请求静态资源,还可能需要请求动态页面. 那么什么是静态资源,什么是动态页面呢? 静态资源 : 例如html文件.图片文件.css.js文件等,都可以算是静态资源 ...
- HTTPS强制安全策略-HSTS协议阅读理解
https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security [阅读理解式翻译,非严格遵循原 ...
- python - wsgi协议
wsgi - python web server gateway interface 出现的目的是,为了在 python框架开发的时候,更具有通用性.只要符合 wsgi标准,就可以自由选择服务器(ng ...
- WSGI协议以及对服务器的影响
下面的内容纯属个人学习心得,如果对于我的观点有疑问,敬请留言,我将虚心向大牛学习. WSGI的全称是WEB SERVICE GATEWAY INTERFACE.WSGI 不是服务器,不是API,也不是 ...
- wsgi 协议
wsgi 协议 前言 本来没打算这么早就学习 wsgi 的,因为想要学习python 是如何处理网络请求的绕不开 wsgi,所以只好先学习一下 wsgi.先对 wsgi 有个印象,到了学习 Djang ...
- WSGI协议解析
WSGI协议中包含两个角色:服务器方和应用程序: 服务器方:其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递 ...
随机推荐
- JQuery Mobile - 固定住页面和页脚
在点击页面空白时候,页眉和页脚会隐藏,在页眉和页脚加入以下代码就可以了: data-tap-toggle ="false" 例子: <div data-role=" ...
- 《Nosql精粹》—— 读后总结
- 1,rocketmq 的原理与安装教程
参考文档 http://blog.csdn.net/a19881029/article/details/34446629 https://github.com/alibaba/RocketMQ htt ...
- 撰写一篇博客要求讲述四则运算2的设计思想,源程序代码、运行结果截图、编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志。
一.撰写一篇博客要求讲述四则运算2的设计思想,源程序代码.运行结果截图.编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志. 1.设计思想: ①创建test.jsp建立第一个前端界面,提 ...
- python实现线性排序算法-计数排序
计数排序假定输入元素的每一个都是介于0到k之间的整数,此处K为某个整数,当k=O(n)时,计数排序的运行时间为O(n) 它的基本思想是:根据每个输入元素x确定小于x的元素个数,根据这个信息把x直接放到 ...
- Liferay7 BPM门户开发之15: Liferay开发体系简介
Liferay SDK 开发体系 主要分6种: Portlet Hook Theme Layout Templates Web Modules Ext Portlet :类似于servlet的web组 ...
- 2019年19道java经典面试题(附答案)
1.不可变对象 指对象一旦被创建状态不能再改变.任何修改都会创建一个新的对象,如 String.Integer及其它包装类. 2.能否创建一个包含可变对象的不可变对象? 可以.不要共享可变对象的引用就 ...
- 安装apache 后,找不到服务,解决办法
在命令行进入安装apache的bin目录下,在输入命令:httpd.exe -k install -n Apache版本号 回车即可注意:要在管理员的身份下进入cmd (C:\Windows\SysW ...
- 课程四(Convolutional Neural Networks),第二 周(Deep convolutional models: case studies) —— 2.Programming assignments : Keras Tutorial - The Happy House (not graded)
Keras tutorial - the Happy House Welcome to the first assignment of week 2. In this assignment, you ...
- Alienware-15-R3 装Ubuntu 16.04.3 LTS
前言:Alienware-15-R3默认安装的系统是win10.现在卸载win0,装Ubuntu 16.04.3 LTS. 一.下载Ubuntu 16.04.3 LTS镜像文件,下载地址:https: ...