[原]浅析WSGI

李余通 18/05/04 13:58:49

关于Python的web application,可以参考PEP-3333。另外,我还找到了一篇翻译,英文有困难的童鞋可以点这里

WSGI

Python Web Server Gateway Interface,翻译过来时Python web服务器网关接口,实际上就是一种协议,我们的应用(Django,Flask)实现了WSGI,就可以配合实现了WSGI(uWSGI,gunicorn)的服务器工作了。
以下是他们的结构图。
这里写图片描述
下面详述两种模式:

两级结构

在这种结构里,uWSGI作为服务器,它用到了HTTP协议以及wsgi协议,flask应用作为application,实现了wsgi协议。当有客户端发来请求,uWSGI接受请求,调用flask app得到相应,之后相应给客户端。
这里说一点,通常来说,Flask等web框架会自己附带一个wsgi服务器(这就是flask应用可以直接启动的原因),但是这只是在开发阶段用到的,在生产环境是不够用的,所以用到了uwsgi这个性能高的wsgi服务器。

三级结构

这种结构里,uWSGI作为中间件,它用到了uwsgi协议(与nginx通信),wsgi协议(调用Flask app)。当有客户端发来请求,nginx先做处理(静态资源是nginx的强项),无法处理的请求(uWSGI),最后的相应也是nginx回复给客户端的。
多了一层反向代理有什么好处?

  • 提高web server性能(uWSGI处理静态资源不如nginx;nginx会在收到一个完整的http请求后再转发给wWSGI)
  • nginx可以做负载均衡(前提是有多个服务器)
  • 保护了实际的web服务器(客户端是和nginx交互而不是uWSGI)

uWSGI

uWSGI是一个实现了wsgi、uwsgi、http协议的服务器。
它有两种模式,http模式对应上面的两次结构,socket模式对应上面的三层结构。
uWSGI的具体使用,不再赘述。

简单的wsgi服务器和wsgi应用

WSGI app

参见PEP-3333,一个基本的wsgi应用,需要实现以下功能:

  1. 必须是一个可调用的对象
  2. 接收两个必选参数environstart_response,以及一个可选参数exc_info。参数名不是固定的,这就意味着你必须使用位置参数而非关键字参数(这应该是用来约束wsgi服务器的)
    • environ存放CGI规定的变量一及别的变量。
    • start_response 是一个可调用对象,通过类似start_response('200 OK',[('Content-Type','text/html'))来发送http的相应头部。
    • exc_info 只有start_response()被错误处理程序调用时,这个参数才会提供,并且是有应用对象提供。
  3. 返回值是字节类型的元组,用来表示http body

来一个简单的wsgi app。

# hello.py
# 函数式 WSGI服务器调用的对象是app
def app(environ,start_response): # 提供两个必须变量
    start_response('200 OK',[('Content-Type','text/html')]) # 调用start_response发送头部
    body = ''
    for i,j in environ.items():
        body += '<p>'+(str(i)+':::::'+str(j)+'<br><p>')
    return [body.encode('utf-8')] # 返回byte的元组表示response body

确实很简单,我们再来看看别的方法实现的app。

# 实现__call__方法的类的实例变量 WSGI服务器调用的对象是app1()
class app1:
    def __call__(self, environ,start_response):
        start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
        body = ''
        for i, j in environ.items():
            body += '<p>' + (str(i) + ':::::' + str(j) + '<br><p>')
        return [body.encode('utf-8')]
# 迭代法 WSGI服务器调用的对象是app2
class app2:
    def __init__(self,environ,start_response):
        self.environ = environ
        self.start_response = start_response

    def __iter__(self):
        self.start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
        body = ''
        for i, j in self.environ.items():
            body += '<p>' + (str(i) + ':::::' + str(j) + '<br><p>')
        yield body.encode('utf-8')

这三种写法不同,但效果是一致的。
看起来很简单,那么怎么调用呢?这两个参数又是怎么提供呢?答案在下边。

WSGI服务器

通常来说,开发WSGI app(Flask、Django)不需要知道WSGI服务器的规则,而WSGI服务器,就是来调用上边的app的,参数自然也是它提供。
WSGI服务器的实现不是本文的重点,有兴趣的小伙伴可以看顶部的两个链接。
Python自己实现了一个WSGI服务器,名为wsgiref,怎么使用呢?见代码:

# ser.py 同hello.py同一层
from wsgiref.simple_server import make_server
from hello import app

# 创建一个服务器,第三个参数是处理函数
# 监听端口及绑定的ip以及请求到来时使用的app
httpd = make_server('0.0.0.0',8000,app)
print('Serving HTTP on port 8000...')
# 开始不断监听HTTP请求
httpd.serve_forever()

运行后,打开浏览器,键入http://127.0.0.1:8000,就能看到我们app的效果了。

作者:baidu_35085676 发表于 2018/05/04 13:58:49 原文链接 https://blog.csdn.net/baidu_35085676/article/details/80184874
阅读:152