WSGI详细介绍

一、WSGI是什么
 
==================================
 

WSGI:Web Server Gateway Interface

WSGI :不是Server,不是python模块,不是python框架,也不是用python编写的软件。 
WSGI:是一个python标准(协议),由 PEP 3333描述。
它用来规定Web Server如何与python应用程序通信。
如果一个Python应用程序(或框架,如Django)符合WSGI标准,那么它可以在任何符合WSGI标准的Server上运行(比如Apache)。
它对Server端和Python应用程序端都做了规定。
 
==================================
 
对Server端的规定:
1、Server必须把http请求以python 字典的形式,作为第一参数传递给python应用程序。这个字典是这样的:

 

{
   'REQUESTMETHOD':'GET',                            # method
   'PATH_INFO':'/path/to/python/application',     #URL
   'QUERY_STRING':'name=bill&age=10',         #参数
   'HTTP_ACCEPT':'text/html',                        #头域
   'HTTP_HOST':'www.mydomain.com',             #头域
   'SERVER_PORT':'8080',                             #Server 端口号
   'REMOTE_ADDR':'192.168.101.2' ,       #客户端IP地址
   .........
}
这个字典内容很多,不一一列举,总之这个字典包含了http请求的所有信息,包括method、URL、参数、头域等,还包含了一些环境信息,比如Server端口号,请求者的ip地址等等。
 
2、Server必须把用来设置http返回码和http响应头域的回调函数作为第二参数传递给python应用程序。此回调函数接受两个参数,第一个参数是http返回码,以字符串形式给出,如:'200 OK';第二个参数是http响应头,以list形式给出,此list里放的是一个一个的tuple,如: [ ('Content-tpye', 'text'), ('Content-Length', '230') ]
 
3、Server从应用程序的返回值得到http响应的消息体。这个返回值必须是一个iterator,Server遍历这个iterator,把里面的所有东西依次返回给客户端浏览器。
 
=======================================
 
对python应用程序端的规定:
1、应用程序必须是一个callable,这个callable接受上述两个参数,作为第一和第二参数;这个callable返回一个iterator,里面存放消息体。
 
2、应用程序从Server传来的第一个参数取请求相关的信息。比如Server传来的第一参数叫environ,想取客户端IP,可以这样:

client_ip = environ['REMOTE_ADDR']

 
3、应用程序利用Server传来的第二个参数为http响应设置返回码和响应头。比如Server传来的第二参数叫set_http,想设置响应码和响应头,可以这样:

set_http('200 OK', ('Content-tpye', 'text'), ('Content-Length', '230'])

 
4、应用程序把响应消息体以返回值的形式交给Server,这个返回值必须是iterator。比如想返回一个打印Hello World的网页,可以这样:

return [r'<html><body><h1>Hello World<h1></body></html>']

注意:虽然字符串本身就是iterator,但上述例子还是将一个字符组包括在了[ ]内部使其成为一个list。这是因为,把字符串作为iterator,Server将会遍历整个字符串,即一个字符一个字符的给客户端发响应,这样影响性能。如果把整个字符串作为list的一个元素,则Server遍历这个list时,把整个字符串一次性返回给客户端,效率高。

 

===========================================

 

二、怎么用WSGI

我们需要做什么:

1、我们写的python应用程序要遵守WSGI对应用程序的规定:即,我们的应用程序是一个callable,接受两个参数,返回一个iterator,如上所述。

2、我们写完符合WSGI规定的应用程序后,把程序安装到符合WSGI规定的服务器上。所谓安装,就是把某个URL跟我们写的某某个python程序对应起来。

 

我们怎么做:

1、写一个符合WSGI规定的应用程序,举两个例子。

例1:假如我们想写一个程序,让访问我们网站的人看到 Hello WSIG !

那么我的程序如下:

 

def application(env, set_http):

        set_http('200 OK', [('Content-type','text/html'),])

        return [r'<html><body><h1>Hello WSGI !</h1></body></html>']

例2:假如我们想写一个程序,让访问我们网站的人看到自己的IP地址

那么我们的程序如下:

 

def application(env, set_http):

        ip = env['REMOTE_ADDR']

        set_http('200 OK', [('Content-type','text/html'),])

        return [r'<html><body><p>Your IP is : %s</p></body></html>'%ip]

以上两个程序都是符合WSGI规定的。

 

2、把程序安装到Server上。这里我们以Apache为例,把以上两个例子安装到Apache上。

安装之前要做个说明:Apache通过mod_wsgi来实现WSGI协议,mod_wsgi要求应用程序的callable必须叫 application,如果叫别的名字需要额外配置。这不是WSGI的规定,这是Apache mod_wsgi的规定。

 

2-1、首先安装mod_wsgi,如果是Apache 2.4 以上的版本,这个mod是默认安装完了的,如果是2.0 或者2.2 版本,需要手动安装。用 yum -y install mod_wsgi 即可安装成功。httpd 2.2 版本安装mod_wsgi的过程中需要注意的是,需要安装httpd-devel 和 python-devel才行,能联网的情况下用yum安装即可,另外python版本要高于2.3

 

2-2、把 Hell WSGI 这个程序安装在 URL /hello 下,只需要在Apache的主配置文件httpd.conf中加入一行:

WSGIScriptAlias /hello /var/www/wsgi-app/hello.py

然后把hello.py扔到/var/www/wsgi-app 目录下就可以了。重启httpd,访问 xx.xx.xx.xx/hello 看到了Hello WSGI !

 

2-3、把显示ip地址的程序安装到 URL /ip 下,跟2-2一样,就不写了。

 

===================================

 

三、我们不要这么做

如果真像上面那样,每写一个程序都安装一下,太麻烦,我们不这么做。

我们用框架开发web应用程序,只要该框架符合WSGI协议。Django是一个符合WSGI的框架。

我们把Django开发好的一个工程安装到 “ / ” 下,其他的URL交由Django处理,只安装一次即可。

 

下面以Django Tutorial 中的 poll 程序 为例,说一下怎么把一个Django工程安装到Apache上。

我们在Apache的根目录(/var/www/)下创建mysite目录,把工程文件都拷贝进去。目录结构如下:

 

然后我们在httpd.conf中加入这2行:

 

WSGIScriptAlias / /var/www/mysite/mysite/wsgi.py

WSGIPythonPath /var/www/mysite

注意1,wsgi.py是我们在创建工程时(django-admin.py startproject)自动生成的

注意2,跟安装自己写的小程序不同,此处要加入2行。第2行是告诉mod_wsgi,你的工程文件夹是可以import的

 

在浏览器里输入  xx.xx.xx.xx/polls 验证基本功能可用
 
在浏览器里输入 xx.xx.xx.xx/admin 发现admin页面打不开,提示:attempt to write a readonly database,把sqlite文件的权限改为777之后,又提示 unable to open database file。在网上找了好久才找打答案:
sqlite 在开始一个事务的时候,需要在数据库文件相同目录下新建一个临时文件,所以,数据库文件所在的路径必须对操作数据库的用户有写权限!把数据库文件以及其所在文件夹的权限都改为 777 后,问题解决。
最后的权限是这样:
/var/www   755
/var/www/mysite 777
/var/www/mysite/sqlite3.db 777
 
当admin的页面打开后,我们发现样子变了,所有的图标,css都没有,样子很丑。
原因是,当年我们开发polls时,使用的是 manage.py runserver ,这个Django的development server。它会自动的为每个installed app 提供静态文件服务。而diango本身不是不为静态文件提供服务的,所以admin的各种静态文件都无法传到客户端浏览器。
 
 
关于静态文件的问题,我们专门写一篇来讨论,这里先不细讲,本篇只关注WSGI。
 
==================  2015.04.16 更新 ===================================
一、
Server传给应用程序的字典里,以 HTTP_ 开头的都是请求头域,比如 HTTP_ACCEPT_ENCODING,,HTTP_CONNECTION,HTTP_USER_AGENT 等等。
但是注意,CONTENT_TYPE 和 CONTENT_LENGTH 不是以 HTTP_ 开头的。
即 Server 要把用户的请求头里,除了CONTENT_TYPE 和 CONTENT_LENGTH  的,都加上 HTTP_ 前缀再交给 python 应用程序。
 
二、
Server 把用户请求的消息体,也放到这个字典里,传给应用程序。对应字典里的 key 是:wsgi.input,它的 value 是一个 file-like object。

 

{
   .........
   'wsgi.input':a-file-like-object ,       #请求消息体
}
除了 wsgi.input 还有几个必须出现在字典里的东西,比如 wsgi.errors, wsgi.multithread 等等,由于不常用,这里就不展开说了。
较常用的就是 wsgi.input ,我们的应用程序可以用 env['wsgi.input'].read() 来读取请求消息体,以便后续处理。
 
 
 
 
 
posted @ 2017-02-27 23:17  王喜山  阅读(895)  评论(0编辑  收藏  举报