使用wsgiref庫diy簡單web架構

1. 瞭解CGI和WSGI

(1)CGIhtml

  CGI(Common Gateway Interface)通用網關接口,即接口協議,前端向服務器發送一個URL(攜帶請求類型、參數、cookie等信息)請求,服務器把這個請求的各類參數寫進進程的環境變量,好比
REQUEST_METHOD,PATH_INFO之類的,而後開啓 cgi模塊之後,將其發送給CGI程序,CGI程序(能夠由各類語言編寫,好比C、C ++、VB 和Delphi 等)從環境變量中解析出各類參數,而後向標準輸出輸出內容(好比cout了一段HTML代碼),這些內容沒有被打印到控制檯上,而是最終響應給了你的瀏覽器,渲染出了網頁。每一次向CGI發送請求,都會生成一個CGI進程,這就是所謂的fork-and-exec模式,這也一般是致使併發瓶頸的癥結,反向代理加上大型的的分佈式系統能夠必定程度上減輕這些壓力。前端

(2)WSGIpython

  WSGI(Python Web Server Gateway Interface,縮寫爲WSGI)web服務器網關接口,也是接口協議,前端向服務器發送一個URL(攜帶請求類型、參數、cookie等信息)請求,服務器把這個請求的各類參數傳給WSGI模塊,wsgi將各類參數進行python化,封裝爲request對象傳遞給按照WSGI接口標準調用註冊的WSGI Application,並返回response參數給客戶端。web

2. wsgiref庫簡介

  wsgiref是python內置庫,實現了一個簡單的WSGI Server和WSGI Application,使用該庫咱們將很容易實現自定義的web架構而不用考慮TCP/HTTP層協議,庫源碼位於/django/lib/wsgiref文件夾,該庫提供了5個模塊:redis

* util -- Miscellaneous useful functions and wrappers

* headers -- Manage response headers

* handlers -- base classes for server/gateway implementations

* simple_server -- a simple BaseHTTPServer that supports WSGI

* validate -- validation wrapper that sits between an app and a server
                  to detect errors in either
                                            

 下面主要對simple_server模塊的重點函數和使用方法進行說明django

3. wsgiref.simple_server類使用及部分源碼分析

(1)先上一段代碼瀏覽器

  該代碼來自simple_server.py的最後7行服務器

if __name__ == '__main__':
    with make_server('', 8000, demo_app) as httpd:
        sa = httpd.socket.getsockname()
        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

  這段代碼表達兩個意思:啓動服務---->處理'http://localhost:8000/xyz?abc'請求cookie

(2)分析make_server啓動服務過程架構

  

  server_class傳入兩個參數,第一個元組(host,ip),第二個WSGIRequestHandler類,第一個參數用於socketserver.TCPServer啓動服務(圖中標記1),第二個參數WSGIRequestHandler用於BaseServer類初始化self.RequestHandlerClass屬性(圖中標記2),用於finish_request()函數進行對象初始化(圖中的標記3),主要目的實現後面回調函數調用

(3)分析handle_request()函數處理過程

  該函數的實現過程在基類BaseServer中,該函數主要實現如下功能(這裏不討論epoll異步併發,相關epoll的內容能夠看我以前的博客):

  get_request()---->verify_request()---->process_request()---->shutdown_request()

  單看函數名就應該明白整個流程了

(4)如今到重點了,看demo_app回調是如何實現的

  接下來主要分析下make_server()函數的第三個參數demo_app是如何實現調用的

  經過源碼咱們看到,finish_request()函數中實現了RequestHandlerClass初始化,也就是WSGIRequestHandler初始化

def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)

  來張圖一看什麼都清楚了

  

  a. 服務啓動後,WSGIServer經過set_app將回調函數保存起來

  b. finish_request()對WSGIRequestHandler進行實例化,調用其基類BaseRequestHandle的構造函數,構造函數中又調用了handle處理函數,因爲派生類WSGIRequestHandler重寫了handle方法,實則調用的是WSGIRequestHandler類的handle函數,如上圖標記3,獲得回調函數對象,並執行。

def run(self, application):
        try:
            self.setup_environ()
            self.result = application(self.environ, self.start_response)
            self.finish_response()
            。。。。。。。。。。。。。。

  到如今爲止一切就都清楚了!接下來就能夠自由的對回調函數進行處理了!

4. DIY web架構

  模仿django架構的model---->route---->view--->template模型實現http請求,顯示網頁

(1)models.py

  使用redis的建立兩個字符串結構name和url

import redis

class Model:
    def __init__(self):
        conn = redis.Redis()
        self.name = conn.get('name')
        self.url = conn.get('url')

(2)urls.py

import views

urlpattern = ((r'/fate0729/', views.login),)

(3)views.py

  實現經過model對模板進行渲染

from models import Model
import re

def render(html_path, **wargvs):
    with open(html_path, 'r') as pf:
        data = ''.join(pf.readlines())
        print(data)

    if wargvs is not None:
        # 爲了測試這裏使用了固定格式obj.attr
        results = re.findall(r'{{(.*)}}', data)
        for result in results:
            obj_attr_list = result.partition('.')
            obj = wargvs.get(obj_attr_list[0])
            attr = obj_attr_list[-1]
            regex = r'{{.*\.%s}}' %(attr)
            value = getattr(obj,attr).decode('utf-8')
            print(attr,value)
            data = re.sub(regex,str(value), data)
    return data.encode('utf-8')

def login(request):
    model = Model()
    return render('web.html', **{'model':model})

(4)main.py

from wsgiref.simple_server import make_server
import urls

def routers():
    urlpattern=urls.urlpattern
    return urlpattern

def applications(environ,start_response):
    path=environ.get("PATH_INFO")
    start_response('200 OK', [('Content-Type', 'text/html'),('Charset', 'utf8')])

    urlpattern=routers()
    func=None
    for item in urlpattern:
        if path==item[0]:
            func=item[1]
            break
    if func:
        return [func(environ)]
    else:
        return [b"<h1>404!<h1>"]

if __name__ == '__main__':
    conn = make_server('127.0.0.1', 8001, applications)
    print('server is working...')
    conn.serve_forever()

(5)測試

  python main.py

  打開瀏覽器輸入:127.0.0.1/fate0729/

  

相關文章
相關標籤/搜索