首先先来了解一下:
WSGI 概念
Web是怎么工作的?
- 浏览器访问url,发送一个HTTP请求。
- 服务器收到请求后生成HTML文档,并把这个文档作为HTTP响应中的Body给浏览器。
- 浏览器解析HTTP响应中的Body生成展示页面。
但是如果要动态生成HTML,则需要一个专用的web服务器,所以一旦一个框架选择了某个web服务器,基本上就不太好更改了,所以定义一个统一的接口极为重要,这就是WSGI接口
一个简单的符合WSGI规范的函数:
from wsgiref.simple_server import make_server
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ['<h1>Hello %s</h1>' % (environ['PATH_INFO'][1:] or 'Web')]
httpd = make_server('127.0.0.1', 8000, application)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
Python内置了一个纯Python编写的测试用的WSGI服务器,wsgiref模块。
application
是符合WSGI规范的一个函数,其中两个参数的作用:
- environ 是一个dict对象,包含了浏览器的所有请求信息。
- start_response 发送HTTP响应的函数。
start_response
函数接受两个参数,第一个为HTTP响应码。第二个为两元组组成的列表,用于表示响应头。
return
中的值作为Body响应给客户端。
打开浏览器输入 127.0.0.1:8000
将会显示:
更多WSGI的介绍,查阅 PEP 333
使用werkzeug创建一个WSGI应用
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
class Fish(object):
def __init__(self, package_name):
self.package_name = package_name
self.debug = False
def dispatch_request(self, request, environ):
return Response('Hello \n%s!\n%s' % (environ or "World", request))
def run(self, host="127.0.0.1", port=8000, **options):
if 'debug' in options:
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options)
def wsgi_app(self, environ, start_response):
request = Request(environ)
#start_response('200 OK', [('Content-Type', 'text/html')])
response = self.dispatch_request(request, environ)
return response(environ, start_response)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def create_app(package_name=None):
app = Fish(package_name)
return app
if __name__ == '__main__':
app = Fish(__name__)
app.run(debug=True)
WSGIServer
好了,上面的例子告诉我们 werkzeug WSGI服务器运行的基础就是werkzeug.serving
当中 run_simple
函数,然后我们顺着它找,发现最终 run_simple()
返回的是一个 *WSGIServer
的实例,然后调用其 serve_forever()
方法,werkzeug中定义了不少WSGIServer的类,有多线程的,有fork进程的,我们暂时分析 servering.py
中一个名为 BaseWSGIServer
的类。
来看一下 BaseWSGIServer
的源码:
try:
from SocketServer import ThreadingMixIn, ForkingMixIn
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
except ImportError:
from socketserver import ThreadingMixIn, ForkingMixIn
from http.server import HTTPServer, BaseHTTPRequestHandler
class BaseWSGIServer(HTTPServer, object):
"""Simple single-threaded, single-process WSGI server."""
multithread = False
multiprocess = False
request_queue_size = 128
def __init__(self, host, port, app, handler=None,
passthrough_errors=False, ssl_context=None):
if handler is None:
handler = WSGIRequestHandler
self.address_family = select_ip_version(host, port)
HTTPServer.__init__(self, (host, int(port)), handler)
self.app = app
self.passthrough_errors = passthrough_errors
self.shutdown_signal = False
if ssl_context is not None:
if isinstance(ssl_context, tuple):
ssl_context = load_ssl_context(*ssl_context)
if ssl_context == 'adhoc':
ssl_context = generate_adhoc_ssl_context()
self.socket = ssl_context.wrap_socket(self.socket,
server_side=True)
self.ssl_context = ssl_context
else:
self.ssl_context = None
def log(self, type, message, *args):
_log(type, message, *args)
def serve_forever(self):
self.shutdown_signal = False
try:
HTTPServer.serve_forever(self)
except KeyboardInterrupt:
pass
finally:
self.server_close()
def handle_error(self, request, client_address):
if self.passthrough_errors:
raise
else:
return HTTPServer.handle_error(self, request, client_address)
def get_request(self):
con, info = self.socket.accept()
return con, info
源码很简单,其实也就是继承了标准库 BaseHTTPServer.HTTPServer ,然后重写了一些方法而已。