研究python,肯定会碰到wsgi这个单词的时候,刚碰到的时候是不是灰常的困惑。没事,我也是,但我有耐心研究,查资料,现在我基本懂了,把看到的资料和自己的理解分享给大家,希望你也能懂?
step 1 简单了解ip/tcp http socket
这一步是必须的,要不然到后面wsgi的时候你会晕的。
我说了简单,所以具体的你可以到官网去深入了解
所以,你只需要知道
(1)ip 协议是规定网络层面的协议
(2)tcp 协议是基于ip协议,规定数据传输层面的协议
(3)http 协议是规定应用层面,建立在ip/tcp基础之上的一种应用,也称作超文本传送协议(Hypertext Transfer Protocol )。我们可以仅使用tcp/ip进行数据的传输,但我们也需要http协议来更好的展示我们的数据。http协议提供了显示数据的具体形式。
(4)socket 封装了ip/tcp协议,提供了一套调用接口。(你一定见到过socket.connect()这种函数,反正我见过),有了socket,你可以更方便的使用ip/tcp协议进行传输数据。
看到这里,你应该有了不小的收获,也有了一些基本的理解。接下来进入第二步。
step 2 通用网关接口 cgi
python 基础教程中的解释:cgi是网络服务器可以将查询传递到专门的程序中并且在网页上显示结果的标准机制。
下面这段话转载某博客,我觉得说的清楚明了。
最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html。事物总是不 断发展,网站也越来越复杂,所以出现动态技术。但是服务器并不能直接运行 php,asp这样的文件,自己不能做,外包给别人吧,但是要与第三做个约定,我给你什么,然后你给我什么,就是握把请求参数发送给你,然后我接收你的处 理结果给客户端。那这个约定就是 common gateway interface,简称cgi。这个协议可以用vb,c,php,python 来实现。cgi只是接口协议,根本不是什么语言。下面图可以看到流程
(http://images.cnitblog.com/blog/353089/201408/222132422994681.gif)
服务器与浏览器之间采用的http传输协议
step 3 wsgi 和它的实现
WSGI(Python Web Server Gateway Interface)就是Python的CGI包装,为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
用Python进行web开发时,你会接触到许多框架,这些框架封装了一些常用的功能方便应用程序的开发。
我们把用flask开发好的python应用程序applyprc部署到一个服务器上,比如说apache,然后我们的服务端就搭建好了。包括一个应用程序加一个服务器程序。
用过Apache的都知道,要启动它监听端口,并将程序与域名绑定。其实这就是一个根据http协议建立连接的过程。
现在我们的服务器程序等待监听客户段发来的请求,然后调用我们相应的应用程序applyprc,返回结果给客户端。
如果现在我们想换一个其他框架开发的applyprc部署到我们的apache上呢? 假如说这个框架没有遵循wsgi,那么我们的apache是不是还得适应你一下?好吧,那过两天我们又换了一个框架,仍没有遵循wsgi,那。。。或许你会成为一个牛逼的服务器开发人员。
如果我们所有web 框架开发人员都遵循wsgi来开发,是不是就没这么多事了,随便你选嘛框架。
WSGI规定:
1、开发的应用程序必须是一个可调用的对象,接受2个参数,然后返回一个可迭代的对象。(到这里可以去看看我之前的迭代器与生成器)
2、服务器程序必须负责配置好应用程序的两个参数,并调用应用程序,迭代访问应用程序返回的结果,然后传回客户端。
只要满足这两点,应用程序与服务器程序就可以配合使用了。
wsgiref是 Python标准库给出的 WSGI的参考实现,可以通过研究它的源码来更深入的了解wsgi。
我大概缕了一遍
#simple_server.py
#demo_app是应用程序,满足wsgi对应用程序的要求
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,'=',`v`
start_response("200 OK", [('Content-Type','text/plain')]) #返回body前 要先返回header所以此处调用一下
return [stdout.getvalue()] #返回的可迭代对象list 返回body内容,通过self.finish_response返回客户端/浏览器
#服务器程序
#make_server函数调用WSGIServer(继承自HTTPServer类)完成了端口的绑定,建立连接,指定处理函数WSGIRequestHandler
def make_server(
host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
#为app在host和port上创建一个新的WSGI服务监听
server = server_class((host, port), handler_class)
#server与处理程序WSGIRequestHandler关联
server.set_app(app)
return server
if __name__ == '__main__':
httpd = make_server('', 8000, demo_app)
sa = httpd.socket.getsockname() #获取正在通信的ip 与端口
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 #响应一次请求然后关闭
#handle_request 经过一系列继承传递最终转化为handle.run(demo_app)
具体可参照下面这位大神画的图
(http://img.blog.csdn.net/20140127182536359?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb25fMXk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
图上的各种继承,如果你了解了step 1,我想对你理解这张图会有很大的帮助。
还记得对服务器程序的要求吗?
# run函数内部实现了对application的调用
def run(self, application):
"""Invoke the application"""
# Note to self: don't move the close()! Asynchronous servers shouldn't
# call close() from finish_response(), so if you close() anywhere but
# the double-error branch here, you'll break asynchronous servers by
# prematurely closing. Async servers must return from 'run()' without
# closing if there might still be output to iterate over.
try:
self.setup_environ() #配置好了参数
self.result = application(self.environ, self.start_response) #调用
self.finish_response()#返回访问结果给客户端 看这里!
except:
try:
self.handle_error()
except:
# If we get an error handling an error, just give up already!
self.close()
raise # ...and let the actual server figure it out.
#配置environ的函数
def setup_environ(self):
"""Set up the environment for one request"""
env = self.environ = self.os_environ.copy()
self.add_cgi_vars()
env['wsgi.input'] = self.get_stdin()
env['wsgi.errors'] = self.get_stderr()
env['wsgi.version'] = self.wsgi_version
env['wsgi.run_once'] = self.wsgi_run_once
env['wsgi.url_scheme'] = self.get_scheme()
env['wsgi.multithread'] = self.wsgi_multithread
env['wsgi.multiprocess'] = self.wsgi_multiprocess
if self.wsgi_file_wrapper is not None:
env['wsgi.file_wrapper'] = self.wsgi_file_wrapper
if self.origin_server and self.server_software:
env.setdefault('SERVER_SOFTWARE',self.server_software)
#检查并发送header的函数
def start_response(self, status, headers,exc_info=None):
"""'start_response()' callable as specified by PEP 333"""
if exc_info:
try:
if self.headers_sent:
# Re-raise original exception if headers sent
raise exc_info[0], exc_info[1], exc_info[2]
finally:
exc_info = None # avoid dangling circular ref
elif self.headers is not None:
raise AssertionError("Headers already set!")
assert type(status) is StringType,"Status must be a string"
assert len(status)>=4,"Status must be at least 4 characters"
assert int(status[:3]),"Status message must begin w/3-digit code"
assert status[3]==" ", "Status message must have a space after code"
if __debug__:
for name,val in headers:
assert type(name) is StringType,"Header names must be strings"
assert type(val) is StringType,"Header values must be strings"
assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
self.status = status
self.headers = self.headers_class(headers)
return self.write #返回head的组装对象。可调用。这里并不返回给客户端,等到在self.finish_response()中发射。
看到这里,我想你应该对wsgi有个大概的了解了吧!写了这么多,希望看到有收获的人能够点个赞,让我有动力继续写下去。