Django源码剖析 WSGI

在python web开发方面,客户端请求服务端,服务端作出响应,返回数据。服务端涉及两部分:服务器(server)和应用程序(application)。WSGI 是python在server和application之间约定的协议。

application/应用程序

def simple_app(environ, start_response):
    """可能是最简单的处理了"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n'] # 返回结果必须可迭代

除了方法以外, 还可以用实现了 call 的类实现.

它会被服务器调用, environ 是一个字典, 包含了环境变量, REQUEST_METHOD,SCRIPT_NAME,QUERY_STRING 等; start_response 是一个回调函数, 会在 simple_app 中被调用, 主要用来开始响应 HTTP. start_response 原型大概是这样:


def start_response(status, response_headers, exc_info=None):
    ...
    return write # 返回这 write 函数 只是为了兼容之前的 web 框架, 新的框架根本用不到.

参数有 status 即状态码; response_headers HTTP 头, 可以修改; exc_info 是与错误相关的信息, 在产生相应数据过程中可能发生错误, 这时需要更新 HTTP 头部, 通过再次调用 start_response 可以实现. 因此更为详尽的实现写法可能是这种:

def start_response(status, response_headers, exc_info=None):
    if exc_info:
         try:
             # do stuff w/exc_info here
         finally:
             exc_info = None    # Avoid circular ref.
    return write

Server/服务器

在服务器方面, 可以想象最简单的工作就是调用 simple_app(), 然后向客户端发送数据:

result = simple_app(environ, start_response) #名字不一定为 simple_app
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()

python wsgiref 模块

Django 是如何实现 WSGI 的. Django 其内部已经自带了一个方便本地测试的小服务器, 所以在刚开始学习 Django 的时候并不需搭建 apache 或者 nginx 服务器. Django 自带的服务器基于 python wsgiref 模块实现, 它自带的测试代码:

# demo_app() 是 application
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()]
 
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
 
if __name__ == '__main__':
    httpd = make_server('', 8000, demo_app)
    sa = httpd.socket.getsockname()
    print "Serving HTTP on", sa[0], "port", sa[1], "..."
    import webbrowser
    webbrowser.open('http://localhost:8000/xyz?abc')
    httpd.handle_request()  # serve one request, then exit

make_server() 中 WSGIServer 类已经作为服务器类, 负责接收请求, 调用 application 的处理, 返回相应;

WSGIRequestHandler 作为请求处理类, 并已经配置在 WSGIServer 中;
接着还设置了 WSGIServer.application 属性(set_app(app));
返回 server 实例.

接着打开浏览器, 即发起请求. 服务器实例 WSGIServer httpd 调用自身 handle_request() 函数处理请求. handle_request() 的工作流程如下:请求–>WSGIServer 收到–>调用 WSGIServer.handle_request()–>调用 _handle_request_noblock()–>调用 process_request()–>调用 finish_request()–>finish_request() 中实例化 WSGIRequestHandler–>实例化过程中会调用 handle()–>handle() 中实例化 ServerHandler–>调用 ServerHandler.run()–>run() 调用 application() 这才是真正的逻辑.–>run() 中在调用 ServerHandler.finish_response() 返回数据–>回到 process_request() 中调用 WSGIServer.shutdown_request() 关闭请求(其实什么也没做)

ps: 明明 application 是 WSGIServer 的属性, 为什么会在 ServerHandler 中调用? 因为在实例化 WSGIRequestHandler 的时候 WSGIServer 把自己搭进去了, 所以在 WSGIRequestHandler 中实例化 ServerHandler 时候可以通过 WSGIRequestHandler.server.get_app() 得到真正的 application.

总结

从上面可以得到, 启动服务器的时候, 无论以什么方式都要给它传递一个 application(), 是一个函数也好, 一个实现了 call 的类也好; 当请求到达服务器的时候, 服务器自会调用 application(), 从而得到相应数据. 至于, 对请求的数据如何相应, application() 中可以细化.

确实, 其中的调用链太过长, 这期间还没有加入 HTTP 头的分析(提取 Cookie等). 如果只为响应一个 “helloworld”, 在 WSGIServer.finish_request() 中直接相应数据就好了, WSGIRequestHandler 和 ServerHandler 类可以直接省去, 而只需要你提供一个 application()! 但事实上, 并不只是相应 “helloworld” 那样简单…

关于 Django 中的 WSGI 如何, 下一节再说. Django 源码剖析从这里开始! 我已经在 github 备份了 Django 源码的注释: Decode-Django, 有兴趣的童鞋 fork 吧. 本文结合 python wsgiref, BaseHTTPServer.py, SocketServer.py 模块源码看更好.

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WSGI(Web Server Gateway Interface)是一个Python标准,定义了Web服务器Python应用程序之间的接口,使得Python应用程序能够在Web服务器上运行。Django中使用WSGI来连接Web服务器Django应用程序。 安装WSGI 要在Django中使用WSGI,你需要先安装WSGI模块。可以通过以下命令安装: ``` pip install mod_wsgi ``` 配置WSGI 安装完成后,需要将WSGI配置到Django应用程序中。以下是在Django中配置WSGI的步骤: 1. 创建WSGI文件 在Django项目的根目录下,创建一个名为`wsgi.py`的文件,并添加以下内容: ``` import os from django.core.wsgi import get_wsgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') application = get_wsgi_application() ``` 这个文件将设置`DJANGO_SETTINGS_MODULE`环境变量,并将Django应用程序与WSGI连接起来。 2. 配置Web服务器 将Web服务器配置为使用WSGI,可以通过以下步骤完成: - Apache HTTP服务器:在Apache配置文件中添加以下内容: ``` WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py WSGIPythonPath /path/to/mysite.com <Directory /path/to/mysite.com/mysite> <Files wsgi.py> Require all granted </Files> </Directory> ``` - Nginx Web服务器:在Nginx配置文件中添加以下内容: ``` location / { include uwsgi_params; uwsgi_pass unix:///path/to/mysite.com/mysite.sock; } ``` 3. 重启Web服务器 将Web服务器重启,使得配置文件生效。 这样,你的Django应用程序就可以通过WSGI与Web服务器交互了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值