最新的工作中,有一部分HTTP API的任务,于是开始折腾Python WSGI…
先恶补理论吧,关于Python WSGI有2篇PEP需要看,PEP 0333和PEP 3333,前者是2003年的提案,后者在2010年对前者做了小幅修订,提案状态也已经是“Final”,所以已经不是“提案”,已经是协议规范了;
WSGI,是“Python Web Server Gateway Interface”的缩写,解决的是各种Web Server(比如Apache、Nginx)与各种Python Web框架(比如Flask、Tornado)之间互联互通的兼容性问题;
如PEP中描述,在没有一个统一协议规范以前,某个Python应用(Web框架、或者应用程序),可能仅支持运行在某一个Web Server下,应用开发者选择某个Web框架的同时,也就绑死了某个Web Server;另外,在某个Web框架写的应用代码,迁移到另外一个Web框架,不一定能跑起来;所以,为了能让用户自由选择、组合使用任意的Web框架和Web Server,该PEP规范了必要的交互方式和数据结构;
注意,WSGI不是一个软件工具,它是一种协议规范的描述,它规范了“交互方式和数据结构”,任何Web Server、Gateway和Framework、Application,只要遵从这个规范去实现,就一定能互联互通;既然是“交互”,就一定至少包含两方吧,PEP指出:
“application端”,必须提供一个“可调用的对象”供“server端”调用,“可调用的对象”一般是个入口函数,应用对HTTP请求的处理,要在这个入口函数内完成,函数最终返回HTTP Respone,比如生成的HTML;
下面的示例摘自PEP 3333:
python
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]
“server端”,一般就是Web Server啦,最主要的任务就是接受到HTTP请求后,调用“application端提供的入口函数(比如上面的“simple_app”),另外应用程序里可能需要很多HTTP请求的信息(比如HTTP Method,GET/POST),这些信息,如何获取呢?需要“server端”在调用应用入口函数的时候作为函数参数传递,就是上面的“environ”,关于“environ”这个数据结构的规范,PEP中也是有详细描述的;
关于“server端”实现的实例代码,稍稍复杂一点,搬运到这里,也不见得帮助理解,自行到PEP 3333查阅吧;
关于Python WSGI的理论介绍,最核心的都在这儿了,更细节的协议介绍,自行查阅吧;
好了,终于可以动手实践了,早已按耐不住啦!我们先把上面的“simple_app”跑起来吧;
bash
$ touch simple_app.py $ vim simple_app.py HELLO_WORLD = b\"Hello world!\\n\" def application(environ, start_response): \"\"\"Simplest possible application object\"\"\" status = \'200 OK\' response_headers = [(\'Content-type\', \'text/plain\')] start_response(status, response_headers) return [HELLO_WORLD]
好了,“application端”已经好了,那“server端”呢?我们需要搞来一个实现了Python WSGI的Web Server,这里,我选择了,uWSGI,不要被它的名字迷惑,它其实是一个功能非常全、实现了各种协议、支持各种的语言的“a full stack for building hosting services”,因为最早支持Python,所以取了这样一个名字,官方都说,名字已经支撑不起它的野心,哈哈;
本实践中,我们要基于Python安装uWSGI:
bash
$ pip install uwsgi
根据你的Python安装路径,uWSGI的二进制程序安装路径也会不同,比我电脑上:
bash
$ ls /usr/local/python2.7/bin/uwsgi
现在,“application端”和“server端”,都已经准备就绪了,我们可以启动的这个简单的WSGI应用了:
bash
uwsgi --http :9090 --wsgi-file simple_app.py
uWSGI会到simple_app.py文件中,找一个固定名字“application”的入口函数,当uWSGI收到HTTP请求时,就会调用该入口函数;
你在浏览器请求http://127.0.0.1:9090,应该会看到uWSGI的输出:
bash
$ uwsgi --http :9090 --wsgi-file simple_app.py [pid: 1492|app: 0|req: 1/1] 127.0.0.1 () {34 vars in 626 bytes} [Sat Jun 20 15:32:14 2015] GET / => generated 11 bytes in 0 msecs (HTTP/1.1 200) 1 headers in 44 bytes (1 switches on core 0) [pid: 1492|app: 0|req: 2/2] 127.0.0.1 () {36 vars in 615 bytes} [Sat Jun 20 15:32:14 2015] GET /favicon.ico => generated 11 bytes in 0 msecs (HTTP/1.1 200) 1 headers in 44 bytes (1 switches on core 0)
此时,你的浏览器应该也已经有“Hello World”页面输出,大功告成!
本文的“Hello World”示例,仅仅是展示Python WSGI的框架结构,“server端”提供的HTTP请求信息“environ”完全没有使用,无脑返回一个不变的字符串;
真正的应用,是需要根据功能要求,定制、填充那个application的入口函数的,应用开发者,可以自己代码实现,也可以依托于一些流行的WSGI框架和Python模块,比如:Flask框架、werkzeug
“server端”就没有什么发挥的空间了,选择一个支持WSGI协议的就好了,就像本文尝试的uWSGI,除非你想自己实现个Web Server;Nginx虽然不直接支持WSGI,但它支持uWSGI,这样把Nginx挡在uWSGI前面,uWSGI就可以专门处理动态的WSGI请求了,形成的架构,非常类似于PHP场景下的Nginx+PHP-FPM;
这些方面的知识,也许会有后续的文章做介绍;
上一篇:python多线程ssh爆破