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 相关文章推荐
python模拟登陆阿里妈妈生成商品推广链接
Apr 03 Python
Python写的一个简单DNS服务器实例
Jun 04 Python
Python实现的一个找零钱的小程序代码分享
Aug 25 Python
用ReactJS和Python的Flask框架编写留言板的代码示例
Dec 19 Python
深入解析Python编程中super关键字的用法
Jun 24 Python
Python3实现的爬虫爬取数据并存入mysql数据库操作示例
Jun 06 Python
python把1变成01的步骤总结
Feb 27 Python
Python增强赋值和共享引用注意事项小结
May 28 Python
python数据库编程 ODBC方式实现通讯录
Mar 27 Python
python自动脚本的pyautogui入门学习
Apr 01 Python
python中def是做什么的
Jun 10 Python
Python 的演示平台支持 WSGI 接口的应用
Apr 20 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
PHP 身份证号验证函数
2009/05/07 PHP
深入Memcache的Session数据的多服务器共享详解
2013/06/13 PHP
ThinkPHP3.2.3实现分页的方法详解
2016/06/03 PHP
页面版文本框智能提示JS代码
2009/11/20 Javascript
javascript设计模式 封装和信息隐藏(上)
2012/07/24 Javascript
sliderToggle在写jquery的计时器setTimeouter中不生效
2014/05/26 Javascript
JavaScript制作淘宝星级评分效果的思路
2020/06/23 Javascript
jQuery图片旋转插件jQueryRotate.js用法实例(附demo下载)
2016/01/21 Javascript
jQuery实现漂亮实用的商品图片tips提示框效果(无图片箭头+阴影)
2016/04/16 Javascript
jQuery 操作input中radio的技巧
2016/07/18 Javascript
微信小程序 火车票查询实例讲解
2016/10/17 Javascript
利用React-router+Webpack快速构建react程序
2016/10/27 Javascript
微信小程序 swiper组件轮播图详解及实例
2016/11/16 Javascript
解析Javascript单例模式概念与实例
2016/12/05 Javascript
canvas实现图像截取功能
2017/02/06 Javascript
js实现随机8位验证码
2020/07/24 Javascript
JS实现的雪花飘落特效示例
2019/12/03 Javascript
javascript canvas实现简易时钟例子
2020/09/05 Javascript
Python正则表达式使用经典实例
2016/06/21 Python
node.js获取参数的常用方法(总结)
2017/05/29 Python
Python3一行代码实现图片文字识别的示例
2018/01/15 Python
浅谈python3.x pool.map()方法的实质
2019/01/16 Python
pytorch sampler对数据进行采样的实现
2019/12/31 Python
Python错误的处理方法
2020/06/23 Python
使用py-spy解决scrapy卡死的问题方法
2020/09/29 Python
网页中的电话号码如何实现一键直呼效果_附示例
2016/03/15 HTML / CSS
个人生活学习自我评价范文
2013/11/26 职场文书
车间组长岗位职责
2013/12/20 职场文书
五十岁生日宴会答谢词
2014/01/15 职场文书
电子信息工程专业推荐信
2014/02/14 职场文书
离婚协议书范文2014
2014/10/16 职场文书
新郎婚礼致辞
2015/07/27 职场文书
CSS中em的正确打开方式详解
2021/04/08 HTML / CSS
python基础学习之递归函数知识总结
2021/05/26 Python
Spring整合Mybatis的全过程
2021/06/28 Java/Android