基础概念

首先要了解 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_simplerun_simple('localhost', 4000, 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_hostnamequit_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 = Nonesrv = 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 forksor 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 = Falsetry:HTTPServer.serve_forever(self)except KeyboardInterrupt:passfinally:self.server_close()
复制代码
###BaseServerdef serve_forever(self, poll_interval=0.5):"""Handle one request at a time until shutdown.Polls for shutdown every poll_interval seconds. Ignoresself.timeout. If you need to do periodic tasks, do them inanother 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 = Falseself.__is_shut_down.set()
复制代码
# BaseServerdef _handle_request_noblock(self):"""Handle one request, without blocking.I assume that selector.select() has returned that the socket isreadable before this function was called, so there should be no risk ofblocking in get_request()."""try:request, client_address = self.get_request()except OSError:returnif 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)raiseelse: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 = 1elif 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_settry: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 orenviron['REQUEST_METHOD'] == 'HEAD' orcode < 200 or code in (204, 304)):self.close_connection = Trueself.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 = Noneelif headers_set:raise AssertionError('Headers already set')headers_set[:] = [status, response_headers]return writedef 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 = Nonetry:execute(self.server.app)except (socket.error, socket.timeout) as e:self.connection_dropped(e, environ)except Exception:if self.server.passthrough_errors:raisefrom werkzeug.debug.tbtools import get_current_tracebacktraceback = 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:passself.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协议,深入理解WSGI

    原标题:深入理解WSGI 前言 Python是一门用途很广泛的也很流程的编程语言,常见的应用有系统编程.爬虫开发.数据分析.人工智能.Web开发等方面,在Web编程方面有一些很成熟的框架供Python ...

  2. 服务器采购框架合同协议书范本,手写一个满足WSGI协议的Server

    在做Web开发时,一个很重要的概念就是服务端和应用程序之间的沟通协议,比如java中的servlet,由于servlet的存在,使得用java开发的web程序既可以跑在tomcat上,也可以是jett ...

  3. 应用服务器与WSGI协议以及flask后端框架总结(后端接收请求返回响应的整个流程)...

    上次遗留了两个问题,先说一下自己的看法 问题: 1.明明一个线程只能处理一个请求,那么栈里的元素永远是在栈顶,那为什么需要用栈这个结构?用普通变量不行吗. 2._request_ctx_stack和_ ...

  4. [转载] Python Web开发最难懂的WSGI协议,到底包含哪些内容? WSGI服务器种类和性能对比

    参考链接: 在Python中创建代理Web服务器 1 http://python.jobbole.com/88653/ 我想大部分Python开发者最先接触到的方向是WEB方向(因为总是有开发者希望马 ...

  5. VRRP协议个人理解(RFC5798)+典型配置+RFC2338/RFC3768文档翻译

    本文档源地址位于RFC 2338: Virtual Router Redundancy Protocol,在此仅为个人学习加深理解使用.转载等操作请保留源文档版权声明. RFC2338存在更新RFC3 ...

  6. 关于GPL协议的理解(开源与商用、免费与收费)

     关于GPL协议的理解(开源与商用.免费与收费) 如果你使用并且修改了我的GPL软件,那么你的软件也必须要开源,否则就不能使用我的软件,你是否把你的软件商用和我没关系.比如,Oracle卖的不是软 ...

  7. nrf52832 学习笔记(七)蓝牙协议层级理解

    nrf52832 学习笔记(七)蓝牙协议层级理解 本文主要由一下几篇文档摘录汇总而成 ,如有错误欢迎斧正 da14531 蓝牙协议文档 深入浅出低功耗蓝牙(BLE)协议栈 低功耗蓝牙ATT/GATT/ ...

  8. Onvif协议:理解什么是Web Services

    ONVIF规范中设备管理和控制部分所定义的接口均以Web Services的形式提供.要理解什么是ONVIF,就必须先知道什么是Web Services.所以,开始介绍ONVIF之前,我单独整理了一篇 ...

  9. TR069协议简单理解

    TR069协议简单理解 tr069 协议代码逻辑整理总结: 1.协议初始化 1) log相关的初始化工作: 2) 读取配置文件,包括协议相关参数的初始化.是否第一次运行(为之后第一次inform做准备 ...

最新文章

  1. Linux 下实现虚拟光驱功能,查看iso文件内容
  2. c sqlserver建表csdn_Oracle拉出在sqlserver建表的语句
  3. Git添加文件改动时出错
  4. OpenCASCADE绘制测试线束:OCAF 命令之几何属性命令
  5. 深度学习tensorflow变量op
  6. vmware配置centos7网络
  7. 造了一个 Redis 分布锁的轮子,没想到还学到这么多东西!!!
  8. ubuntu 发数据给usb_【奇怪的知识】USB 镜像刻录知识点
  9. Kali学习 | 密码攻击:6.10 创建密码字典
  10. js 计算当前时间到下个整点时间,差多少分钟,多少秒
  11. Line 第三方登录 后台
  12. 你用过猿如意吗?猿如意可以使用ChatGPT哦,这里详细介绍了猿如意的功能,为什么我建议你使用猿如意,来看看吧
  13. 嵌入式软件开发基础问题
  14. wps转换成word如何实现?不妨试试这两个小技巧
  15. DNS内网欺骗(仅供参考)
  16. php 数字 字母,怎么使用php实现数字转字母
  17. 胖哥食品网络诊断分析
  18. 【华人学者风采】林学民 新南威尔士大学
  19. Java-用集合编写的班级管理系统
  20. Java之协变返回类型理解和简单实例

热门文章

  1. 《Linux 性能及调优指南》1.4 硬盘I/O子系统
  2. 【leetcode】535. Encode and Decode TinyURL
  3. Struts学习之自定义拦截器
  4. shell脚本定义日志输出函数
  5. 在线JWT Token解析解码
  6. 在线CSS代码压缩美化工具
  7. java实例属性_Java 静态属性与实例属性的初始化
  8. Hyper-V Server 虚拟光纤通道
  9. 微软发布ASP.NET 5路线图
  10. bilibili弹幕转ass