1. 程式人生 > >網站搭建筆記精簡版---廖雪峰WebApp實戰-Day2:編寫Web App骨架筆記

網站搭建筆記精簡版---廖雪峰WebApp實戰-Day2:編寫Web App骨架筆記

網站搭建筆記精簡版-廖雪峰教程學習@[三川水祭]
僅作學習交流使用,將來的你會感謝現在拼命努力的自己!!!

目錄

什麼是IO
非同步IO的背景知識
Web app骨架搭建
asyncio函式與aiohttp函式詳解

什麼是IO

IO意思是輸入與輸出,本地是記憶體,外地是磁碟或網頁,本地往外地傳送資料叫做輸出Output,本地接收外地傳送的資料叫做Input。IO包括同步IO與非同步IO。舉個栗子,背景是記憶體往磁碟寫資料,記憶體輸出這個資料需要0.1s,而磁碟接收這個資料需要10s
同步IO:當記憶體輸出完資料後,處於等待狀態,等磁碟接收完資料後繼續執行接下來的步驟
非同步IO

:當記憶體輸出完資料後,懶得等磁碟慢悠悠的寫,就直接搞別的事情去了,等磁碟寫完後,通知記憶體,然後記憶體才與磁碟繼續往下執行專案。
而磁碟通知記憶體有兩種方式,分別為回撥模式與輪詢模式。
回撥模式:磁碟直接過去呼叫記憶體,開展下一步工作。
輪詢模式:磁碟發個資訊給記憶體,告訴記憶體已經完成工作,可開展下一步工作了,而記憶體在搞別的事情的時候則需要時不時的看一下訊息,確定磁碟是否完成。當接收到訊息後,才與磁碟共同開展下一步工作。

非同步IO背景知識

面臨問題:CPU執行速度高而IO裝置執行速度低(龜速)。
解決方法:多執行緒和多程序、非同步IO。
多執行緒的缺點:當執行緒數量較多時候,CPU資源主要用線上程之間的切換上,真正搞程式碼的資源少,效能嚴重下降。
程序

:系統進行資源分配和排程的一個獨立單位,有自己的記憶體空間
執行緒:是CPU排程和分派的基本單位,共享程序記憶體資源。
協程:使用yield函式,不斷的在子程式之間進行切換的單條執行緒。是一種輕量級的執行緒,可以在不加鎖的情況下訪問全域性變數。
迭代器:iter函式,迭代的物件從第一個元素開始訪問,直到所有元素訪問完後才可結束,或者通過StopIteration強制結束。物件可以使列表、元組和字串。
生成器:yield函式,執行到當前進行阻塞,n = yield r的意思是當前函式接收到資料後將值傳遞給n,若n為None,則不執行,反之向下執行迴圈到該條函式處,阻塞,返回r,等待接收新一輪n值。
迭代器與生成器參考
該網頁
。網頁下的評論有更詳細的解釋。

Web app骨架搭建

該部分使用asyncio處理非同步IO程式設計,實現單執行緒併發操作。進而使用aiohttp網路框架建立TCP網路伺服器,不斷使用輪詢模式接收瀏覽器相應,通過解析瀏覽器發過來的請求,尋找相應的coroutine類執行函式,返回相應的Body response。coroutine類表示可併發且帶有生成器的執行函式。

import logging; logging.basicConfig(level=logging.INFO)

import asyncio, os, json, time
from datetime import datetime

from aiohttp import web

# 將生成器index函式標記為coroutine
async def index(request):
    # 相應的格式,注意加上content_type='text/html'屬性,否則會顯示為下載檔案。
    return web.Response(body=b'<h1>Awesome</h1>', content_type='text/html')

async def init(loop):
    app = web.Application(loop=loop)
    # 連結index函式與‘/'物件,當接收到GET '/'時候,延遲兩秒,伺服器返回Index。
    app.router.add_route('GET', '/', index)
    # 建立TCP服務,等待請求。
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000)
    # 生成日誌資訊
    logging.info('server started at http://127.0.0.1:9000...')
    return srv

# 使用輪詢模式保證協程執行
loop = asyncio.get_event_loop()
# s使用init函式建立TCP服務,等待瀏覽器連結,並使用相關函式進行相應。
loop.run_until_complete(init(loop))
# 一直等著瀏覽器的連結請求。
loop.run_forever()

asyncio函式與aiohttp函式詳解

asyncio函式:實現非同步IO程式設計的函式。可利用一下程式碼進行實驗。可參考本網頁

import asyncio

# async等價於@asyncio.coroutine,將wget這個generator函式標記為coroutine物件
# 之後將該協程物件扔到get_event_loop函式中執行,實現非同步IO操作。
async def wget(host):
    print('wget %s...' % host)
    connect = asyncio.open_connection(host, 80)
    # 呼叫connect,返回讀與寫兩個IO,分別負責本地接收網頁傳送的訊息與向網頁傳送訊息兩種模式。
    # await類似於yield from生成器
    reader, writer = await connect
    header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
    writer.write(header.encode('utf-8'))
    # drain函式就是將write到快取區的東西flush一下,提交給網頁。
    await writer.drain()
    while True:
        # 迴圈讀取網頁返回來的資訊
        line = await reader.readline()
        # 可檢視讀取的具體資訊是什麼
        #print(line)
        # 當讀到'\r\n'時候代表網頁相應頭已經讀取完成,之後就是body部分。
        if line == b'\r\n':
            break
        print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
    # Ignore the body, close the socket
    writer.close()

# 使用輪詢模式保證協程執行
loop = asyncio.get_event_loop()
# 將多個coroutine物件封裝成一個Task任務列表
tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
# 使用asyncio.wait(task)將任務併發執行。
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

aiohttp:aiohttp則是基於asyncio實現的HTTP框架,直接呼叫裡面的函式就可使實現簡單的本地HTTP伺服器搭建。可利用一下程式碼進行實驗。想了解具體機制的參考本網頁,想了解函式中add_route機制的參考本網頁

import logging; logging.basicConfig(level=logging.INFO)
import asyncio

from aiohttp import web

# 將生成器index函式標記為coroutine
async def index(request):
    # 延時兩秒後繼續執行之後的步驟
    await asyncio.sleep(2)
    # 相應的格式,注意加上content_type='text/html'屬性,否則會顯示為下載檔案。
    return web.Response(body=b'<h1>Index</h1>', content_type='text/html')

# 將生成器hello函式標記為coroutine
async def hello(request):
    # 延時兩秒後繼續執行之後的步驟
    await asyncio.sleep(2)
    text = '<h1>hello, %s!</h1>' % request.match_info['name']
    # 相應的格式,head會自動生成,自己負責寫body即可。
    return web.Response(body=text.encode('utf-8'), content_type='text/html')

# 將生成器init函式標記為coroutine
async def init(loop):
    app = web.Application(loop=loop)
    # 連結index函式與‘/'物件,當接收到GET '/'時候,延遲兩秒,伺服器返回Index。
    app.router.add_route('GET', '/', index)
    # 連結index函式與‘/hello/{name}'物件,當接收到GET '/hello/{name}'時候,
    # 延遲兩秒,伺服器取出{name}物件,並構造新的body進行返回。
    app.router.add_route('GET', '/hello/{name}', hello)
    # 建立TCP服務,等待請求。
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
    # 顯示日誌資訊。
    logging.info('server started at http://127.0.0.1:9000...')
    return srv

# 使用輪詢模式保證協程執行
loop = asyncio.get_event_loop()
# s使用init函式建立TCP服務,等待瀏覽器連結,並使用相關函式進行相應。
loop.run_until_complete(init(loop))
# 一直等著瀏覽器的連結請求。
loop.run_forever()

在瀏覽器輸入localhost:8000,等兩秒,網頁會接收到返回資訊並顯示為index。接下來在瀏覽器輸入localhost:8000/hello/handsome,等兩秒,網頁顯示hello, handsome!。
參考部落格
廖雪峰的官方網站
python之aiohttp原始碼解析——add_route和middleware的工作方式
程序、執行緒、協程之概念理解