Django源码分析(1) —— WSGI协议

一、概念介绍

1、WSGI:全称是Web Server Gateway Interface(Web服务器网关接口) 。WSGI不是服务器、python模块、框架、API或者任何软件,它只是一种规范,描述了web server如何与web application通信。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Tornado、Flask、Django。

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

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


WSGI协议主要包括serverapplication两部分: WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端; WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。 WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django、Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。

二、协议实现

1、Web Server部分实现:Web server针对从HTTP客户端接收的每个request调用一次Web application。为了说明这一点,这里有一个简单的CGI网关服务的函数实现。

py 复制代码
import os, sys

enc, esc = sys.getfilesystemencoding(), 'surrogateescape'

def unicode_to_wsgi(u):
    return u.encode(enc, esc).decode('iso-8859-1')

def wsgi_to_bytes(s):
    return s.encode('iso-8859-1')

def run_with_cgi(application):
    environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}
    environ['wsgi.input']        = sys.stdin.buffer
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1, 0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True

    if environ.get('HTTPS', 'off') in ('on', '1'):
        environ['wsgi.url_scheme'] = 'https'
    else:
        environ['wsgi.url_scheme'] = 'http'

    headers_set = []
    headers_sent = []

    def write(data):
        out = sys.stdout.buffer

        if not headers_set:
             raise AssertionError("write() before start_response()")

        elif not headers_sent:
             # Before the first output, send the stored headers
             status, response_headers = headers_sent[:] = headers_set
             out.write(wsgi_to_bytes('Status: %s\r\n' % status))
             for header in response_headers:
                 out.write(wsgi_to_bytes('%s: %s\r\n' % header))
             out.write(wsgi_to_bytes('\r\n'))

        out.write(data)
        out.flush()

    def start_response(status, response_headers, exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[1].with_traceback(exc_info[2])
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")

        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)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        if hasattr(result, 'close'):
            result.close()

2、Web Application 部分的实现:Web application是一个接受两个参数的可调用对象,函数、方法、类或具有 __call__方法的实例都可以用作application对象。Web application对象必须能够被多次调用,因为几乎所有服务器/网关都会发出重复请求。

py 复制代码
HELLO_WORLD = b"Hello world!\n"

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [HELLO_WORLD]

class AppClass:
    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
相关推荐
进击的雷神10 分钟前
多展会框架复用、Next.js结构统一、北非网络优化、参数差异化配置——阿尔及利亚展爬虫四大技术难关攻克纪实
javascript·网络·爬虫·python
ZTLJQ11 分钟前
网络通信的基石:Python HTTP请求库完全解析
开发语言·python·http
华科大胡子17 分钟前
爬虫对抗:ZLibrary反爬机制实战分析
python
进击的小头19 分钟前
第17篇:卡尔曼滤波器之概率论初步
python·算法·概率论
是梦终空19 分钟前
计算机毕业设计269—基于python+深度学习+YOLOV8的交通标志识别系统(源代码+数据库+报告)
python·深度学习·opencv·毕业设计·torch·课程设计·pyqt5
crossoverJie26 分钟前
OpenAI 收购 Python 工具链 uv 和 Ruff
开发语言·人工智能·python·uv
2401_8318249630 分钟前
RESTful API设计最佳实践(Python版)
jvm·数据库·python
龙文浩_30 分钟前
AI / 机器学习 / 深度学习,它们的关系、核心流程、算法、任务、训练逻辑
人工智能·python·深度学习·神经网络·机器学习
NGC_661130 分钟前
深入理解 Java 线程池:从原理到实战
java·开发语言·python
研究点啥好呢31 分钟前
3月24日GitHub热门项目推荐|让AI无所不能
人工智能·python·开源·github