mvc框架打造笔记之wsgi协议的优缺点以及接口实现


Posted in Python onAugust 01, 2018

前言:

又是WSGI ,这是我曾经比较熟悉的协议,以前针对实现了wsgi server的unicorn和uwsgi都写过源码解析的文章。  其实他们的实现也很简单,就是给flask、django这样的application传递environ,start_response 。

mvc框架打造笔记之wsgi协议的优缺点以及接口实现

什么是WSGI协议,什么是WSGI Server,他们的区别是什么?

上线的架构图很容易误导别人,乍一看有nginx这样的web服务器,又有gunicorn这样的wsgi server。  我们先说明wsgi 跟 wsgi server的关系,wsgi是个协议,是web底层跟application解耦的协议。wsgi server是自己做web服务器借用wsgi协议来调用application。 我们需要明确一点,nginx是无法直接跟flask application做通信,需要借用wsgi server。flask本身也有个web服务器是werkzeug,so 才能启动服务并监听端口。记得以前uwsgi没名气的时候,我们都在使用apache + mode_wsgi模式,apache也无法直接跟tornado通信,是借用mod_wsgi把torando做成了unix socket服务,说白了也是启动了一个服务,靠apache来转发。

nginx、apache在这里只是启动了proxy的作用,那为什么不直接把uwsgi和gunicorn给暴露出去,因为nginx的静态文件处理能力极强。

mvc框架打造笔记之wsgi协议的优缺点以及接口实现

WSGI怎么工作的

wsgi主要是两层,服务器方 和 应用程序 :

1  服务器方:从底层解析http解析,然后调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.

2  应用程序:用来生成返回的header,body和status,以便返回给服务器方。

WSGI把来自socket的数据包解析为http格式,然后进而变化为environ变量,这environ变量里面有wsgi本身的信息(比如 host, post,进程模式等),还有client的header及body信息。start_respnse是一个函调函数,必须要附带两个参数,一个是status(http状态),response_headers(响应的header头信息)。

像flask、django、tornado都会暴露WSGI协议入口,我们只需要自己实现WSGI协议,wsgi server然后给flask传递environ,及start_response, 等到application返回值之后,我再socket send返回客户端。

WSGI的优点、缺点是什么?

优点:

多样的部署选择和组件之间的高度解耦

由于上面提到的高度解耦特性,理论上,任何一个符合WSGI规范的App都可以部署在任何一个实现了WSGI规范的Server上,这给Python Web应用的部署带来了极大的灵活性。

Flask自带了一个基于Werkzeug的调试用服务器。根据Flask的文档,在生产环境不应该使用内建的调试服务器,而应该采取以下方式之一进行部署:

GUNICORN

UWSGI

缺点:

没有

我们在wsgi层可以做什么时尚的操作:

  1. 黑白名单规则防御.
  2. 可以通过重写环境变量,根据目标URL,将请求消息路由到不同的应用对象。这意思就是说,实现一套类似nginx location proxy的规则,可以把阻塞高性能转给tornado的app. 当然这是理想化的操作.
  3. 允许在一个进程中同时运行多个应用程序或应用框架.
  4. 负载均衡和远程处理,通过在网络上转发请求和响应消息.

我们用python具体实现这个wsgi server及协议.

#xiaorui.cc
 
import socket
import StringIO
import sys
 
class WSGIServer(object):
 
 address_family = socket.AF_INET
 socket_type = socket.SOCK_STREAM
 request_queue_size = 1
 
 def __init__(self, server_address):
  # 创建一个可用的socket
  self.listen_socket = listen_socket = socket.socket(
   self.address_family,
   self.socket_type
  )
  #socket的工作模式
  listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  # Bind
  listen_socket.bind(server_address) #绑定地址
  # Activate
  listen_socket.listen(self.request_queue_size) #监听文件描述符
  # Get server host name and port
  host, port = self.listen_socket.getsockname()[:2]
  self.server_name = socket.getfqdn(host)
  self.server_port = port
  self.headers_set = []
 
 def set_app(self, application):
  self.application = application
 
 def serve_forever(self): #启动WSGI server服务,不停的监听并获取socket数据。
  listen_socket = self.listen_socket
  while True:
    self.client_connection, client_address = listen_socket.accept()
   self.handle_one_request() #处理新连接
 
 def handle_one_request(self): #主要处理函数
  self.request_data = request_data = self.client_connection.recv(1024)
  print(''.join(
   '< {line}\n'.format(line=line)
   for line in request_data.splitlines()
  ))
 
  self.parse_request(request_data)
  env = self.get_environ()
 
  #给flask\tornado传递两个参数,environ,start_response
  result = self.application(env, self.start_response)
 
  # Construct a response and send it back to the client
  self.finish_response(result)
 
 def parse_request(self, text): #处理socket的http协议
  request_line = text.splitlines()[0]
  request_line = request_line.rstrip('\r\n')
  # Break down the request line into components
  (self.request_method, # GET
   self.path,   # /hello
   self.request_version # HTTP/1.1
   ) = request_line.split()
 
 def get_environ(self): #获取environ数据
  env = {}
  env['wsgi.version']  = (1, 0)
  env['wsgi.url_scheme'] = 'http'
  env['wsgi.input']  = StringIO.StringIO(self.request_data)
  env['wsgi.errors']  = sys.stderr
  env['wsgi.multithread'] = False
  env['wsgi.multiprocess'] = False
  env['wsgi.run_once']  = False
  env['REQUEST_METHOD'] = self.request_method # GET
  env['PATH_INFO']   = self.path    # /hello
  env['SERVER_NAME']  = self.server_name  # localhost
  env['SERVER_PORT']  = str(self.server_port) # 8888
  return env
 
 def start_response(self, status, response_headers, exc_info=None): #创建回调函数.
  server_headers = [
   ('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),
   ('Server', 'WSGIServer 0.3'),
  ]
  self.headers_set = [status, response_headers + server_headers]
 
 def finish_response(self, result): #把application返回给WSGI的数据返回给客户端。
  try:
   status, response_headers = self.headers_set
   response = 'HTTP/1.1 {status}\r\n'.format(status=status)
   for header in response_headers:
    response += '{0}: {1}\r\n'.format(*header)
   response += '\r\n'
   for data in result:
    response += data
   # Print formatted response data a la 'curl -v'
   print(''.join(
    '> {line}\n'.format(line=line)
    for line in response.splitlines()
   ))
   self.client_connection.sendall(response)
  finally:
   self.client_connection.close()
 
SERVER_ADDRESS = (HOST, PORT) = '', 8888
 
def make_server(server_address, application):
 server = WSGIServer(server_address)
 server.set_app(application)
 return server
 
if __name__ == '__main__':
 if len(sys.argv) < 2:
  sys.exit('Provide a WSGI application object as module:callable')
 app_path = sys.argv[1]
 module, application = app_path.split(':')
 module = __import__(module) #动态加载模块
 application = getattr(module, application) #使用自省的模式加载application的WSGI协议入口。
 httpd = make_server(SERVER_ADDRESS, application)
 print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
 httpd.serve_forever()

下面是flask application的实例, 我们会发现python的常见web框架都兼容了wsgi接口,没有例外。

#xiaorui.cc
from flask import Flask
from flask import Response
flask_app = Flask('flaskapp')
 
@flask_app.route('/search')
def hello_world():
 return Response(
  'xiaorui.cc Golang vs python !\n',
  mimetype='text/plain'
 )
 
app = flask_app.wsgi_app

运行方式很简单:

python webserver2.py flaskapp:app

这样一个wsgi就构成了,下次我们会借用这wsgi框架扩展成prefork wsgi server,类似gunicorn那样。   以前在wsgi做过一些application的分流,但涉及到高并发的场景下的分流,还是建议直接在nginx层面做。 现在nginx lua的编程能力越来越强,大家都在使用nginx lua做网关及入口的开发。

参考文章:

https://ruslanspivak.com/lsbaws-part2/

总结

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

Python 相关文章推荐
Python网页解析利器BeautifulSoup安装使用介绍
Mar 17 Python
利用Python演示数型数据结构的教程
Apr 03 Python
使用python实现个性化词云的方法
Jun 16 Python
详解使用 pyenv 管理多个版本 python 环境
Oct 19 Python
深入理解Python中的*重复运算符
Oct 28 Python
Python装饰器用法示例小结
Feb 11 Python
OpenCV-Python 摄像头实时检测人脸代码实例
Apr 30 Python
Python GUI编程 文本弹窗的实例
Jun 11 Python
python利用tkinter实现屏保
Jul 30 Python
Django框架模板用法入门教程
Nov 04 Python
4行Python代码生成图像验证码(2种)
Apr 07 Python
Django restful framework生成API文档过程详解
Nov 12 Python
python爬虫自动创建文件夹的功能
Aug 01 #Python
浅谈关于Python3中venv虚拟环境
Aug 01 #Python
python Web开发你要理解的WSGI &amp; uwsgi详解
Aug 01 #Python
Django教程笔记之中间件middleware详解
Aug 01 #Python
flask框架中勾子函数的使用详解
Aug 01 #Python
flask中过滤器的使用详解
Aug 01 #Python
Python拼接微信好友头像大图的实现方法
Aug 01 #Python
You might like
php设计模式 Facade(外观模式)
2011/06/26 PHP
php模拟ping命令(php exec函数的使用方法)
2013/10/25 PHP
getJSON跨域SyntaxError问题分析
2014/08/07 PHP
PHP模拟post提交数据方法汇总
2016/02/16 PHP
面向对象的Javascript之一(初识Javascript)
2012/01/20 Javascript
javascript对select标签的控制(option选项/select)
2013/01/31 Javascript
单击和双击事件的冲突处理示例代码
2014/04/03 Javascript
教你使用javascript简单写一个页面模板引擎
2015/05/05 Javascript
详解Bootstrap的aria-label和aria-labelledby应用
2016/01/04 Javascript
JavaScript基础之静态方法和实例方法分析
2018/12/26 Javascript
详解nodejs 开发企业微信第三方应用入门教程
2019/03/12 NodeJs
一文搞懂ES6中的Map和Set
2019/05/20 Javascript
新手如何快速理解js异步编程
2019/06/24 Javascript
微信js-sdk 录音功能的示例代码
2019/11/01 Javascript
javascript设计模式之装饰者模式
2020/01/30 Javascript
Python实现在matplotlib中两个坐标轴之间画一条直线光标的方法
2015/05/20 Python
Python自动化部署工具Fabric的简单上手指南
2016/04/19 Python
深入解答关于Python的11道基本面试题
2017/04/01 Python
Django内容增加富文本功能的实例
2017/10/17 Python
Python中多个数组行合并及列合并的方法总结
2018/04/12 Python
详谈python在windows中的文件路径问题
2018/04/28 Python
Python subprocess模块功能与常见用法实例详解
2018/06/28 Python
Python 实现「食行生鲜」签到领积分功能
2018/09/26 Python
Python Cookie 读取和保存方法
2018/12/28 Python
python实现转盘效果 python实现轮盘抽奖游戏
2019/01/22 Python
Python判断对象是否相等及eq函数的讲解
2019/02/25 Python
突破canvas语法限制 让他支持链式语法
2012/12/24 HTML / CSS
在使用非全零作为空指针内部表达的机器上, NULL是如何定义
2014/11/09 面试题
护理专业个人求职简历的自我评价
2013/10/13 职场文书
战友聚会主持词
2014/04/02 职场文书
村委会换届选举方案
2014/05/03 职场文书
群众路线领导对照材料
2014/08/23 职场文书
住房抵押登记委托书
2014/09/27 职场文书
承兑汇票转让证明怎么写?
2014/11/30 职场文书
Python数据结构之队列详解
2022/03/21 Python
python使用pycharm安装pyqt5以及相关配置
2022/04/22 Python