零、參考
https://www.jianshu.com/p/679dee0a4193
https://www.letiantian.me/2015-09-10-understand-python-wsgi/
一、對於web服務的理解
web服務應該至少包含兩個模塊:web服務器和web應用程序,兩個模塊在功能和代碼上解耦。
web服務器負責處理socket調用、http數據解析和封裝等底層操作。
web應用程序負責業務處理、數據增刪改查、頁面渲染/生成等高層操作。
web服務器一旦接收到http請求,經過自身的解析后就會調用web應用程序來處理業務邏輯,並得到web應用程序的返回值,再經過自身的封裝發送給客戶端。
二、對於wsgi協議的理解
在web服務器和web應用程序之間需要定義一個接口規則,這也叫協議,用於明確兩者之間以什么樣的形式交互數據。即:web服務器應該以什么樣的形式調用web應用程序,而web應用程序又應該定義成什么形式。
python下規定的web服務的接口規則叫做wsgi,wsgi協議對於server和application的接口定義如下:
對於server調用規則的定義:
response = application(environ, start_response)
對於application接口編碼的定義:
def application(environ, start_response):
status = '200 OK'
response_headers = [('Content-Type', 'text/plain'),]
start_response(status, response_headers)
return [b'hello',]
只要是遵從如上形式進一步封裝server和application的,均稱為實現了wsgi協議的server/application。
python內置提供了一個wsigref模塊用於提供server,但是只能用於開發測試,django框架就是使用此模塊作為它的server部分,也就說,實際生產中的server部分,還需要使用其他模塊來實現。
任何web框架,可能沒有實現server部分或者只實現一個簡單的server,但是,web框架肯定實現了application部分。application部分完成了對一次請求的全流程處理,其中各環節都可以提供豐富的功能,比如請求和響應對象的封裝、model/template的實現、中間件的實現等,讓我們可以更加細粒度的控制請求/響應的流程。

三、自定義一個簡單的基於wsgi協議的web框架
django框架的server部分由python內置的wsgiref模塊提供,我們只需要編寫application應用程序部分。
from wsgiref.simple_server import make_server
def app(environ, start_response): # wsgi協議規定的application部分的編碼形式,可在此基礎上擴展
status = '200 OK'
respones_headers = []
start_response(status, response_headers)
return [b'hello',]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8080, app)
httpd.serve_forever()
用以下圖示表示簡單的web請求流程架構(偽代碼)

web服務器就像是一顆心臟不停的跳動,驅動整個web系統為用戶提供http訪問服務,並調用application返回響應
四、django中的server實現
django使用的底層server模塊是基於python內置的wsgiref模塊中的simple_server,每次django的啟動都會執行如下run函數。run函數中會執行serve_forever,此步驟將會啟動socket_server的無限循環,此時就可以循環提供請求服務,每次客戶端請求到來,服務端就執行django提供的application模塊。
django中server的啟動----django.core.servers.basehttp.py
"""
HTTP server that implements the Python WSGI protocol (PEP 333, rev 1.21).
Based on wsgiref.simple_server which is part of the standard library since 2.5.
This is a simple server for use in testing or debugging Django apps. It hasn't
been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE!
"""
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
if threading:
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
# ThreadingMixIn.daemon_threads indicates how threads will behave on an
# abrupt shutdown; like quitting the server by the user or restarting
# by the auto-reloader. True means the server will not wait for thread
# termination before it quits. This will make auto-reloader faster
# and will prevent the need to kill the server manually if a thread
# isn't terminating correctly.
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
底層無限循環將作為web服務的主要驅動----socektserver.py
def serve_forever(self, poll_interval=0.5):
"""Handle one request at a time until shutdown.
Polls for shutdown every poll_interval seconds. Ignores
self.timeout. If you need to do periodic tasks, do them in
another thread.
"""
self.__is_shut_down.clear()
try:
# XXX: Consider using another file descriptor or connecting to the
# socket to wake this up instead of polling. Polling reduces our
# responsiveness to a shutdown request and wastes cpu at all other
# times.
with _ServerSelector() as selector:
selector.register(self, selectors.EVENT_READ)
while not self.__shutdown_request:
ready = selector.select(poll_interval)
if ready:
self._handle_request_noblock()
self.service_actions()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
server對於application的調用----wsgiref.handlers.py
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.
五、django中的application實現
django的application模塊是通過WSGIHandler的一個實例來提供的,此實例可以被call,然后根據wsgi的接口規則傳入environ和start_response。所以本質上,django就是使用的內置python提供的wsgiref.simple_server再對application進行豐富的封裝。大部分的django編碼工作都在application部分。
application的編碼定義部分----django.core.handlers.wsgi.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase)
response_headers = list(response.items())
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
start_response(status, response_headers)
if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
response = environ['wsgi.file_wrapper'](response.file_to_stream)
return response
六、django的底層調用鏈

七、總結
web服務是基於socket的高層服務,所以web服務必須含有web服務器這一模塊。
web服務需要動態渲染數據,需要中間件來豐富功能,需要封裝和解析來處理數據,所以web服務必須含有web應用程序這一模塊。
web框架是一種工具集,封裝了各種功能的底層代碼,提供給我們方便開發的接口。但不論是哪一種框架,它們的底層原理基本都是一致的。
應該深入學習、研究一個web框架,精通一門框架的實現原理和設計理念。
