欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浅析WSGI

程序员文章站 2022-03-23 17:58:38
...

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

WSGI

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

两级结构

在这种结构里,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的效果了。

相关标签: python WSGI