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实现的各种排序算法代码
Mar 04 Python
python发送邮件示例(支持中文邮件标题)
Feb 16 Python
解析Python中的生成器及其与迭代器的差异
Jun 20 Python
简单谈谈Python流程控制语句
Dec 04 Python
Django2.1集成xadmin管理后台所遇到的错误集锦(填坑)
Dec 20 Python
对Python定时任务的启动和停止方法详解
Feb 19 Python
Python按钮的响应事件详解
Mar 04 Python
Python第三方包PrettyTable安装及用法解析
Jul 08 Python
python实现简单的五子棋游戏
Sep 01 Python
利用Python实现Json序列化库的方法步骤
Sep 09 Python
python 实现客户端与服务端的通信
Dec 23 Python
python 单机五子棋对战游戏
Apr 28 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
40年前的这部特摄片恐龙特级克塞号80后的共同回忆
2020/03/08 日漫
Linux中用PHP判断程序运行状态的2个方法
2014/05/04 PHP
Yii2 assets清除缓存的方法
2016/05/16 PHP
php实现的SSO单点登录系统接入功能示例分析
2016/10/12 PHP
PHP 的Opcache加速的使用方法
2017/12/29 PHP
Laravel关系模型指定条件查询方法
2019/10/10 PHP
jQuery中delegate()方法用法实例
2015/01/19 Javascript
javascript的BOM汇总
2015/07/16 Javascript
老生常谈js动态添加事件--- 事件委托
2016/07/19 Javascript
AngularJS基础 ng-repeat 指令简单示例
2016/08/03 Javascript
jQuery插件HighCharts绘制2D饼图效果示例【附demo源码下载】
2017/03/21 jQuery
jQuery中clone()函数实现表单中增加和减少输入项
2017/05/13 jQuery
React从react-router路由上做登陆验证控制的方法
2018/05/10 Javascript
vue 设置路由的登录权限的方法
2018/07/03 Javascript
React中使用UEditor百度富文本的方法
2018/08/22 Javascript
小程序点击图片实现自动播放视频
2020/05/29 Javascript
JavaScript中AOP的实现与应用
2019/05/06 Javascript
Vue-cli3项目引入Typescript的实现方法
2019/10/18 Javascript
javascript this指向相关问题及改变方法
2020/11/19 Javascript
Python连接mssql数据库编码问题解决方法
2015/01/01 Python
Pycharm学习教程(6) Pycharm作为Vim编辑器使用
2017/05/03 Python
Python实现的生产者、消费者问题完整实例
2018/05/30 Python
Python实现账号密码输错三次即锁定功能简单示例
2019/03/29 Python
值得收藏的10道python 面试题
2019/04/15 Python
python 中的列表生成式、生成器表达式、模块导入
2019/06/19 Python
python 5个实用的技巧
2020/09/27 Python
Weblogic和WebSphere不同特点
2012/05/09 面试题
中职生自荐信
2013/10/13 职场文书
大一学生职业生涯规划
2014/03/11 职场文书
承诺书样本
2014/08/30 职场文书
住房抵押登记委托书
2014/09/27 职场文书
2014年团支部年度工作总结
2014/12/24 职场文书
暖春观后感
2015/06/08 职场文书
如何才能写好调研报告?
2019/07/03 职场文书
农村房屋租赁合同(范本)
2019/07/23 职场文书
导游词之天下银坑景区
2019/11/21 职场文书