python Web开发你要理解的WSGI & uwsgi详解


Posted in Python onAugust 01, 2018

WSGI协议

首先弄清下面几个概念:

WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Bottle, Flask, Django。

uwsgi:与WSGI一样是一种通信协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快。

uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。

WSGI协议主要包括server和application两部分:

  • WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;
  • WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。

WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django,Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。

python Web开发你要理解的WSGI & uwsgi详解

像Django,Flask框架都有自己实现的简单的WSGI server,一般用于服务器调试,生产环境下建议用其他WSGI server。

WSGI协议的实现

以Django为例,分析一下WSGI协议的具体实现过程。

django WSGI application

WSGI application应该实现为一个可调用对象,例如函数、方法、类(包含`call`方法)。需要接收两个参数:

  • 一个字典,该字典可以包含了客户端请求的信息以及其他信息,可以认为是请求上下文,一般叫做environment(编码中多简写为environ、env)
  • 一个用于发送HTTP响应状态(HTTP status )、响应头(HTTP headers)的回调函数

通过回调函数将响应状态和响应头返回给server,同时返回响应正文(response body),响应正文是可迭代的、并包含了多个字符串。下面是Django中application的具体实现部分:

class WSGIHandler(base.BaseHandler):
 initLock = Lock()
 request_class = WSGIRequest

 def __call__(self, environ, start_response):
 # 加载中间件
 if self._request_middleware is None:
 with self.initLock:
 try:
 # Check that middleware is still uninitialized.
 if self._request_middleware is None:
 self.load_middleware()
 except:
 # Unload whatever middleware we got
 self._request_middleware = None
 raise

 set_script_prefix(get_script_name(environ))
 # 请求处理之前发送信号
 signals.request_started.send(sender=self.__class__, environ=environ)
 try:
 request = self.request_class(environ)
 except UnicodeDecodeError:
 logger.warning('Bad Request (UnicodeDecodeError)',
 exc_info=sys.exc_info(),
 extra={'status_code': 400,})
 response = http.HttpResponseBadRequest()
 else:
 response = self.get_response(request)

 response._handler_class = self.__class__

 status = '%s %s' % (response.status_code, response.reason_phrase)
 response_headers = [(str(k), str(v)) for k, v in response.items()]
 for c in response.cookies.values():
 response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
 # server提供的回调方法,将响应的header和status返回给server
 start_response(force_str(status), response_headers)
 if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
 response = environ['wsgi.file_wrapper'](response.file_to_stream)
 return response

可以看出application的流程包括:

  • 加载所有中间件,以及执行框架相关的操作,设置当前线程脚本前缀,发送请求开始信号;
  • 处理请求,调用get_response()方法处理当前请求,该方法的的主要逻辑是通过urlconf找到对应的view和callback,按顺序执行各种middleware和callback。
  • 调用由server传入的start_response()方法将响应header与status返回给server。
  • 返回响应正文

django WSGI Server

负责获取http请求,将请求传递给WSGI application,由application处理请求后返回response。以Django内建server为例看一下具体实现。

通过runserver运行django项目,在启动时都会调用下面的run方法,创建一个WSGIServer的实例,之后再调用其serve_forever()方法启动服务。

def run(addr, port, wsgi_handler, ipv6=False, threading=False):
 server_address = (addr, port)
 if threading:
 httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
 else:
 httpd_cls = WSGIServer
 # 这里的wsgi_handler就是WSGIApplication
 httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
 if threading:
 httpd.daemon_threads = True
 httpd.set_app(wsgi_handler)
 httpd.serve_forever()

下面表示WSGI server服务器处理流程中关键的类和方法。

python Web开发你要理解的WSGI & uwsgi详解

.WSGIServer

run()方法会创建WSGIServer实例,主要作用是接收客户端请求,将请求传递给application,然后将application返回的response返回给客户端。

  • 创建实例时会指定HTTP请求的handler:WSGIRequestHandler类
  • 通过set_app和get_app方法设置和获取WSGIApplication实例wsgi_handler
  • 处理http请求时,调用handler_request方法,会创建WSGIRequestHandler实例处理http请求。
  • WSGIServer中get_request方法通过socket接受请求数据

.WSGIRequestHandler

  • 由WSGIServer在调用handle_request时创建实例,传入request、cient_address、WSGIServer三个参数,__init__方法在实例化同时还会调用自身的handle方法
  • handle方法会创建ServerHandler实例,然后调用其run方法处理请求

.ServerHandler

  • WSGIRequestHandler在其handle方法中调用run方法,传入self.server.get_app()参数,获取WSGIApplication,然后调用实例(__call__),获取response,其中会传入start_response回调,用来处理返回的header和status。
  • 通过application获取response以后,通过finish_response返回response

.WSGIHandler

  • WSGI协议中的application,接收两个参数,environ字典包含了客户端请求的信息以及其他信息,可以认为是请求上下文,start_response用于发送返回status和header的回调函数

虽然上面一个WSGI server涉及到多个类实现以及相互引用,但其实原理还是调用WSGIHandler,传入请求参数以及回调方法start_response(),并将响应返回给客户端。

django simple_server

django的simple_server.py模块实现了一个简单的HTTP服务器,并给出了一个简单的demo,可以直接运行,运行结果会将请求中涉及到的环境变量在浏览器中展示出来。

其中包括上述描述的整个http请求的所有组件:

ServerHandler, WSGIServer, WSGIRequestHandler,以及demo_app表示的简易版的WSGIApplication。

可以看一下整个流程:

if __name__ == '__main__':
 # 通过make_server方法创建WSGIServer实例
 # 传入建议application,demo_app
 httpd = make_server('', 8000, demo_app)
 sa = httpd.socket.getsockname()
 print("Serving HTTP on", sa[0], "port", sa[1], "...")
 import webbrowser
 webbrowser.open('http://localhost:8000/xyz?abc')
 # 调用WSGIServer的handle_request方法处理http请求
 httpd.handle_request() # serve one request, then exit
 httpd.server_close()
 
def make_server(
 host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
 """Create a new WSGI server listening on `host` and `port` for `app`"""
 server = server_class((host, port), handler_class)
 server.set_app(app)
 return server

# demo_app可调用对象,接受请求输出结果
def demo_app(environ,start_response):
 from io import StringIO
 stdout = StringIO()
 print("Hello world!", file=stdout)
 print(file=stdout)
 h = sorted(environ.items())
 for k,v in h:
 print(k,'=',repr(v), file=stdout)
 start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')])
 return [stdout.getvalue().encode("utf-8")]

demo_app()表示一个简单的WSGI application实现,通过make_server()方法创建一个WSGIServer实例,调用其handle_request()方法,该方法会调用demo_app()处理请求,并最终返回响应。

uWSGI

uWSGI旨在为部署分布式集群的网络应用开发一套完整的解决方案。主要面向web及其标准服务。由于其可扩展性,能够被无限制的扩展用来支持更多平台和语言。uWSGI是一个web服务器,实现了WSGI协议,uwsgi协议,http协议等。
uWSGI的主要特点是:

  • 超快的性能
  • 低内存占用
  • 多app管理
  • 详尽的日志功能(可以用来分析app的性能和瓶颈)
  • 高度可定制(内存大小限制,服务一定次数后重启等)

uWSGI服务器自己实现了基于uwsgi协议的server部分,我们只需要在uwsgi的配置文件中指定application的地址,uWSGI就能直接和应用框架中的WSGI application通信。

参考阅读:

  • WSGI & uwsgi
  • WSGI Tutorial
  • 打造mvc框架之wsgi协议的优缺点及接口实现
  • Nginx和uWSGI通信机制
  • 理解Python WSGI

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
对pandas中apply函数的用法详解
Apr 10 Python
Python获取昨天、今天、明天开始、结束时间戳的方法
Jun 01 Python
Python实现八皇后问题示例代码
Dec 09 Python
python UDP(udp)协议发送和接收的实例
Jul 22 Python
解决pycharm启动后总是不停的updating indices...indexing的问题
Nov 27 Python
tensorflow实现在函数中用tf.Print输出中间值
Jan 21 Python
Python实现获取当前目录下文件名代码详解
Mar 10 Python
Python参数传递机制传值和传引用原理详解
May 22 Python
Python定义一个函数的方法
Jun 15 Python
python给list排序的简单方法
Dec 10 Python
python编程的核心知识点总结
Feb 08 Python
用python实现监控视频人数统计
May 21 Python
Django教程笔记之中间件middleware详解
Aug 01 #Python
flask框架中勾子函数的使用详解
Aug 01 #Python
flask中过滤器的使用详解
Aug 01 #Python
Python拼接微信好友头像大图的实现方法
Aug 01 #Python
Python Flask前后端Ajax交互的方法示例
Jul 31 #Python
Python常见内置高效率函数用法示例
Jul 31 #Python
Python使用jsonpath-rw模块处理Json对象操作示例
Jul 31 #Python
You might like
风格模板初级不完全修改教程
2006/10/09 PHP
sql注入与转义的php函数代码
2013/06/17 PHP
php  PATH_SEPARATOR判断当前服务器系统类型实例
2016/10/28 PHP
php原生导出excel文件的两种方法(推荐)
2016/11/19 PHP
phpstorm 配置xdebug的示例代码
2019/03/31 PHP
jQuery :nth-child前有无空格的区别分析
2011/07/11 Javascript
IE6下拉框图层问题探讨及解决
2014/01/03 Javascript
ie8下修改input的type属性报错的解决方法
2014/09/16 Javascript
关于javascript中dataset的问题小结
2015/11/16 Javascript
JavaScript、jQuery与Ajax的关系
2016/01/24 Javascript
jQuery实现根据滚动条位置加载相应内容功能
2016/07/18 Javascript
jQuery返回定位插件详解
2017/05/15 jQuery
Bootstrap table使用方法记录
2017/08/23 Javascript
微信小程序swiper组件用法实例分析【附源码下载】
2017/12/07 Javascript
vue 项目打包通过命令修改 vue-router 模式 修改 API 接口前缀
2018/06/13 Javascript
浅谈微信小程序列表埋点曝光指南
2019/10/15 Javascript
vue子传父关于.sync与$emit的实现
2019/11/05 Javascript
解决Echarts 显示隐藏后宽度高度变小的问题
2020/07/19 Javascript
微信小程序实现简单购物车功能
2020/12/30 Javascript
[22:20]初生之犊-TI4第5名LGD战队纪录片
2014/08/13 DOTA
Python多进程分块读取超大文件的方法
2016/04/13 Python
深入了解Python数据类型之列表
2016/06/24 Python
python设计模式大全
2016/06/27 Python
Python如何快速上手? 快速掌握一门新语言的方法
2017/11/14 Python
tensorflow实现KNN识别MNIST
2018/03/12 Python
Python如何爬取微信公众号文章和评论(基于 Fiddler 抓包分析)
2019/06/28 Python
Python3 sys.argv[ ]用法详解
2019/10/24 Python
python 实现rolling和apply函数的向下取值操作
2020/06/08 Python
Python3爬虫ChromeDriver的安装实例
2021/02/06 Python
css3中背景尺寸background-size详解
2014/09/02 HTML / CSS
html5通过postMessage进行跨域通信的方法
2017/12/04 HTML / CSS
ASOS英国官网:英国在线时装和化妆品零售商
2017/05/19 全球购物
碧欧泉Biotherm加拿大官方网站:法国高端护肤品牌
2019/10/18 全球购物
建筑个人求职信范文
2014/01/25 职场文书
安全伴我行演讲稿
2014/09/04 职场文书
黄埔军校观后感
2015/06/10 职场文书