深圳男生快快乐乐

python 学习中

WSGI介绍:Python 首先了解

1.1 什么是WSGI

首先介绍几个关于WSGI相关的概念
WSGI:全称是Web Server Gateway InterfaceWSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范。serverapplication的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Torando,Flask,Django。

uwsgi:与WSGI一样是一种通信协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快。

uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。

WSGI协议主要包括serverapplication两部分

1、WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;
2、WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。

WSGI协议其实是定义了一种serverapplication解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGIGunicorn都是实现了WSGI server协议的服务器,DjangoFlask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。

1.2 怎么实现WSGI

上文说过,实现WSGI协议必须要有wsgi server和application,因此,我们就来实现这两个东西。

我们来看看官方WSGI使用WSGI的wsgiref模块实现的小demo

def demo_app(environ,start_response):  
    from StringIO import StringIO  
    stdout = StringIO()  
    print >>stdout, "Hello world!"  
    print >>stdout  
    h = environ.items(); h.sort()  
    for k,v in h:  
        print >>stdout, k,'=', repr(v)  
    start_response("200 OK", [('Content-Type','text/plain')])  
    return [stdout.getvalue()]  
  
httpd = make_server('localhost', 8002,  demo_app)  
httpd.serve_forever()  # 使用select  

实现了一个application,来获取客户端的环境和回调函数两个参数,以及httpd服务端的实现,我们来看看make_server的源代码

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

接受一系列函数,返回一个server对象,实现还是比较简单,下面我们来看看在django中如何实现其自身的wsgi服务器的。

下面我们自己来实现一遍:
WSGI 规定每个 python 程序(Application)必须是一个可调用的对象(实现了__call__ 函数的方法或者类),接受两个参数 environ(WSGI 的环境信息) 和 start_response(开始响应请求的函数),并且返回 iterable。几点说明:

environ 和 start_response 由 http server 提供并实现
environ 变量是包含了环境信息的字典
Application 内部在返回前调用 start_response
start_response也是一个 callable,接受两个必须的参数,status(HTTP状态)和 response_headers(响应消息的头)
可调用对象要返回一个值,这个值是可迭代的。

 # 1. 可调用对象是一个函数
def application(environ, start_response):
 
   response_body = 'The request method was %s' % environ['REQUEST_METHOD']
 
   # HTTP response code and message
   status = '200 OK'
 
   # 应答的头部是一个列表,每对键值都必须是一个 tuple。
   response_headers = [('Content-Type', 'text/plain'),
                       ('Content-Length', str(len(response_body)))]
 
   # 调用服务器程序提供的 start_response,填入两个参数
   start_response(status, response_headers)
 
   # 返回必须是 iterable
   return [response_body]    
   
# 2. 可调用对象是一个类
class AppClass:
    """这里的可调用对象就是 AppClass 这个类,调用它就能生成可以迭代的结果。
        使用方法类似于: 
        for result in AppClass(env, start_response):
             do_somthing(result)
    """
 
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response
 
    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"
 
# 3. 可调用对象是一个实例 
class AppClass:
    """这里的可调用对象就是 AppClass 的实例,使用方法类似于: 
        app = AppClass()
        for result in app(environ, start_response):
             do_somthing(result)
    """
 
    def __init__(self):
        pass
 
    def __call__(self, environ, start_response):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"

服务器程序端

上面已经说过,标准要能够确切地实行,必须要求程序端和服务器端共同遵守。上面提到, envrion 和 start_response 都是服务器端提供的。下面就看看,服务器端要履行的义务。

准备 environ 参数
定义 start_response 函数
调用程序端的可调用对象

import os, sys
 
def run_with_cgi(application):    # application 是程序端的可调用对象
    # 准备 environ 参数,这是一个字典,里面的内容是一次 HTTP 请求的环境变量
    environ = dict(os.environ.items())
    environ['wsgi.input']        = sys.stdin
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1, 0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True            
    environ['wsgi.url_scheme'] = 'http'
 
    headers_set = []
    headers_sent = []
 
    # 把应答的结果输出到终端
    def write(data):
        sys.stdout.write(data)
        sys.stdout.flush()
 
    # 实现 start_response 函数,根据程序端传过来的 status 和 response_headers 参数,
    # 设置状态和头部
    def start_response(status, response_headers, exc_info=None):
        headers_set[:] = [status, response_headers]
          return write
 
    # 调用客户端的可调用对象,把准备好的参数传递过去
    result = application(environ, start_response)
    
    # 处理得到的结果,这里简单地把结果输出到标准输出。
    try:
        for data in result:
            if data:    # don't send headers until body appears
                write(data)
    finally:
        if hasattr(result, 'close'):
            result.close()

 

posted on 2024-03-04 21:44  深圳男生快快乐乐  阅读(22)  评论(0编辑  收藏  举报

导航