乐趣区

深入浅出wsgi

前言

置信每个刚接触 python web 开发的同学,在学习 flask、django 这些框架的时候,必定会被告知说正式环境部署时,不应该用框架自带的 web 服务器启动对外提供服务,而应该搭配专门的 wsgi 服务器(uwsgi、gunicorn 等)提供服务,为什么?在这套举荐的部署计划中,wsgi 服务器和咱们应用的框架都各自扮演着什么角色?本文将尽可能用大白话通俗易懂的讲清楚。

为什么须要 wsgi

首先,先思考一个问题,一个 http 申请从客户端发动到解决实现须要进过哪些流程?

服务端的解决能够分为两局部:

  • 与客户端进行交互(接管并解析 request,封装并发送 response)
  • 依据 request 参数进行相干的解决

python 将这两局部离开来,即有专门解决客户端交互的 gateway 框架。而 flask、django 这些 web 框架,则是专门用于进行申请逻辑解决的工作,称为 application。

离开后,再看上图,步骤 2、4、5 是由 gateway 框架负责;步骤 3 由 application 负责。

大家都晓得,pythoner 圈里个个都是人才, 谈话又好听,技术还强的不行,很快就涌现出各种各样优良的 gateway 框架和 application 框架了,那怎么能让这些框架实现“混搭”呢?
咱们能够规定一个协定,或者说是一个对 gateway 框架和 application 的设计要求;规定 gateway 和 application 别离都要须要实现什么办法,办法要怎么调用;这样,当市面上呈现一个更好的 gateway 框架时,咱们能够零老本的将咱们的 application 跟新的 gateway 搭配起来。

而这套协定,就是 wsgi,简略说,它规定了 gateway 如何和 application 交互

wsgi 具体是如何定义的

wsgi 对于 server 端有以下要求:

1. 提供一个接管客户端申请的服务

  1. 提供一个 start_response 函数,被 application 调用,start_response 函数接管 status 和 headers,用于设置响应状态和响应头部信息(def start_response(self, status, headers,exc_info=None):
  2. 将 application 返回的后果发送给客户端

Server 端实现伪码

class WsgiServer:
    def __init__(self, host, port, app):
    def serve_forever(self):    # 启动监听端口服务,接管申请
    def start_response(self, status, headers):  # 用于被 application 调用,设置 status 和 headers
        ...
        self.status = status
        self.headers = headers
        ...
    def handle_request(self):   # 申请处理函数
        environ = {             # 申请相干变量
            "wsgi.input": self.stdin  # request 内容
            "wsgi.version": self.wsgi_version # wsgi 版本信息
            ...
        }
        iterResult = self.app(environ, self.start_response)     # 业务逻辑解决申请,返回可迭代对象

        for data in iterResult:
            self.write(data)        # 将数据挨个发送到缓存区

        self.send_data()            # 将后果返回给客户端 

对于 application 有以下要求:

  1. 必须是一个可调用对象,并且接管 environ、start_response 两个参数;environ 为 server 端解析的以后申请环境变量;
  2. 返回值是一个可迭代对象

application 的简略示例

def application(environ, start_response):    # 接管 environ、start\_response

    start_response('200 OK', [('Content-Type', 'text/html')])    # 调用 start\_response 用于设置响应状态和响应头部信息

    return [b'<h1>Hello, web!</h1>']    # 返回一个可迭代对象,作为响应内容 
退出移动版