Sanic教程:1.快速開始
看程序員有趣的文章教程!
快速開始
在安裝Sanic之前,讓我們一起來看看Python在支持非同步的過程中,都經歷了哪些比較重大的更新。
首先是Python3.4版本引入了,這讓Python有了支持非同步IO的標準庫,而後3.5版本又提供了兩個新的關鍵字,目的是為了更好地標識非同步IO,讓非同步編程看起來更加友好,最後3.6版本更進一步,推出了穩定版的,從這一系列的更新可以看出,Python社區正邁著堅定且穩重的步伐向非同步編程靠近。
1.安裝
Sanic是一個支持語法的非同步無阻塞框架,這意味著我們可以依靠其處理非同步請求的新特性來提升服務性能,如果你有框架的使用經驗,那麼你可以迅速地使用來構建出心中想要的應用,並且性能會提升不少,我將同一服務分別用Flask和Sanic編寫,再將壓測的結果進行對比,發現Sanic編寫的服務大概是的1.5倍。
僅僅是Sanic的非同步特性就讓它的速度得到這麼大的提升么?是的,但這個答案並不標準,更為關鍵的是Sanic使用了作為的事件循環,由Cython編寫,它的出現讓更快,快到什麼程度?這篇文章中有介紹,其中提出速度至少比 nodejs、gevent 和其他Python非同步框架要快兩倍,並且性能接近於用Go編寫的程序,順便一提,Sanic的作者就是受這篇文章影響,這才有了Sanic。
怎麼樣?有沒有激起你學習Sanic的興趣,如果有,就讓我們一起開始學習吧,在開始之前,你只需要有一台安裝了Python的電腦即可。
說明:由於Windows下暫不支持安裝uvloop,故在此建議使用Mac或Linux
2.虛擬環境
程序世界一部分是對應著現實的,在生活中,我們會在不同的環境完成不同的任務,比如在廚房做飯、卧室休息,分工極其明確。
其實用Python編寫應用服務也是如此,它們同樣希望應用服務與開發環境是一對一的關係,這樣做的好處在於,每個獨立的環境都可以簡潔高效地管理自身對應服務所依賴的第三方庫,如若不然,各個服務都安排在同一環境,這樣不僅會造成管理上的麻煩,還會使第三方庫之間產生衝突。
通過上面的敘述,我們是不是可以得出這樣一個核心觀點:應該在不同的環境下做不同的事,以此類推,寫項目的時候,我們也需要為每個不同的項目構建一個無干擾的的環境,發散思維,總結一下:
不同的項目,需要為其構建不同的虛擬環境,以免互相干擾
構建虛擬環境的工具很多,如下:
…...
以上三個工具都可以快速地幫助我們構建當前需要的Python環境,如果你之前沒有使用過,可直接點開鏈接進行下載,如果你正在使用其它的環境管理工具,也不要緊,因為不論你使用哪一種方式,我們最終目的都是針對一個新項目構建一個新的環境。
安裝配置好之後,簡單看看官方提供的使用方法,就可以開始了,比如我本機使用的是,安裝完成後可以很方便地創建一個虛擬環境,比如這裡使用Python3.6來作為本書項目的默認環境:
# 新建一個python3.6環境conda create --name python36 python=3.6# 安裝好之後 輸入下面命令進入名為python36的環境sourceactivate python36
若安裝速度比較慢,可以考慮換國內源,比如國內鏡像,至於為什麼選擇python3.6作為默認環境,一是因為Sanic只支持Python3.5+,二則是我們構建的項目最終是要在生產環境下運行的,所以建議最好安裝Python3.6下穩定版本的。
3.安裝Sanic
Python安裝第三方模塊都是利用工具進行安裝,這裡也不例外,首先進入上一步我們新建的虛擬環境,然後安裝:
# 安裝Sanic,請先使用 source activate python36 進入虛擬環境pip install sanic# 如果不想使用uvloop和ujson 可以這樣安裝SANIC_NO_UVLOOP=trueSANIC_NO_UJSON=true pip install sanic
通過上面的命令,你就可以在虛擬環境中安裝Sanic以及其依賴的第三方庫了,若想查看Sanic是否已經正確安裝,可以進入終端下對應的虛擬環境,啟動Python解釋器,導入Sanic庫:
# 啟動Python解釋器python>>> import sanic>>>
如果沒有出現錯誤,就說明你已經正確地安裝了Sanic,請繼續閱讀下一節,了解下如何利用Sanic來構建一個Web項目吧。
4.踏出第一步
我們將正式使用Sanic來構建一個web項目,讓我們踏出第一步,利用Sanic來編寫一個返回字元串的服務程序。
新建一個文件,名為:
#!/usr/bin/env pythonfromsanicimportSanicfromsanic.responseimporttextapp=Sanic()@app.route("/")asyncdeftest(request):returntext("Hello World!")if__name__=="__main__": app.run(host="0.0.0.0",port=8000)
Sanic的目標是讓編寫服務更加簡單易用,請看上面僅用不到10行的代碼,就編寫好了一個簡單的Web服務,運行此文件,在瀏覽器輸入,出現的字元會讓你回想起當年學c的恐懼^_^。
如果你是第一次使用Sanic,上面的代碼可能會讓你產生一些困擾,不用擔心,接下來,我們將一起用Sanic編寫一個簡單的資訊閱讀的web服務,在這過程中,你將逐漸地了解到Sanic的一些基本用法,如路由的構建、接受請求數據以及返迴響應的內容等。
本次示例的源代碼全部在github上,見examples/demo01/news.py。
5.編寫一個資訊閱讀項目
在開始編寫之前,第一步最好寫一下需求,哪怕是個簡單不過的玩具項目也不能略過這個步驟,比如現在編寫的資訊閱讀項目,需求就一個,在頁面中展示一些資訊新聞。
既然是展示資訊新聞,那麼解決數據來源的問題最為重要,對於這個問題你也不用擔心,因為在本次示例的源碼中我編寫了一個名為的函數專門用來返回資訊新聞數據,簡化代碼如下:
asyncdefget_news(size=10):""" Sanic是一個非同步框架,為了更好的發揮它的性能,有些操作最好也要用非同步的 比如這裡發起請求就必須要用非同步請求框架aiohttp 所以使用本服務的時候請先執行: pip install aiohttp 數據使用的是readhub網站的api介面 為了使這個數據獲取函數正常運行,我會保持更新,所以具體代碼:examples/demo01/news.py"""asyncwithaiohttp.ClientSession()asclient:asyncwithclient.get(readhub_api,params=params,headers=headers)asresponse:assertresponse.status==200text=awaitresponse.json()returntext
這樣各位就可以只專註於Sanic的代碼實現,而不必考慮其他問題,我會一直維護這個數據獲取函數,以保證數據正常輸出,各位請放心使用。
6.構建路由
數據的問題解決之後,我們可以開始著手於需求的實現了,根據前面的描述,此時的需求是當客戶端(Web瀏覽器)訪問的時候,瀏覽器會立馬展示服務端響應返回的10條資訊新聞(假設內容由index()函數返回),若瀏覽器訪問的是,此時返回的就是第二頁的10條資訊新聞,以此類推......
當Sanic程序實例接收到一個請求,比如前面提到的,它是怎麼知道這個URL可以對應到函數呢?
Sanic有一個機制來保存URL和函數(一般稱之為視圖函數)之間的映射關係,就像中和,這樣當服務端接收到請求,就會立馬知道,接下來需要調用函數了,我們將其稱之為路由。
Sanic中可以用修飾器來定義路由,當Sanic服務啟動的時候,就會將其中傳入的參數與裝飾的函數自動註冊好,比如下面這段代碼:
@app.route("/")asyncdefindex(request):"""當服務端接收到客戶端的/請求時,就會調用此函數"""returntext("Hello World!")
此時請求就會返回,很顯然,這不是我們想要的需求,我們的需求是展示10條資訊新聞,數據怎麼來?你只需要調用函數,就會獲取到你想要的資訊數據:
@app.route("/")asyncdefindex(request):# html頁面模板html_tem="""
"""html_list=[]# 獲取數據all_news=awaitget_news()# 生成在瀏覽器展示的html頁面foreach_newsinall_news: html_list.append(html_tem.format(href=each_news.get("news_info", [{}])[].get("url","#"),title=each_news.get("title"),summary=each_news.get("summary"),updated_at=each_news.get("updated_at"), ))returnhtml("".join(html_list))
運行此服務:
python run news.py
此時,訪問,你就會獲得Sanic服務程序返回的資訊新聞,如下圖,可以看到返回服務端提供的最新資訊:
頁面成功地呈現出我們想要的結果,實在是令人興奮,等等,不能高興太早,我們還有一個需求,要根據瀏覽器輸入的頁數來展示內容,如:,思考一下,應該怎樣優雅地完成這個需求,或許你會想,再構建一對URL與視圖函數的映射關係,像下面這樣:
@app.route("/2")asyncdefpage_2(request):
不得不說,這是一個糟糕的解決方案,這樣沒法解決接下來的第3頁、第4頁、甚至第n頁(雖然目前這個服務程序只展示到第2頁),最佳實踐應該是把頁數當做變數來獲取,Sanic的路由機制自然提供了獲取動態請求參數的功能,如下:
@app.route("/
")@app.route("/")asyncdefindex(request, page=1):""" 支持/請求與/page請求方式 具體的代碼邏輯也會有一點改變,可參考:examples/demo01/news.py"""
再次運行此服務:
python run news.py
不論是請求或者,都是我們想要的結果。
7.請求數據
細心的你可能會發現,每次編寫一個視圖函數的時候,總是有一個參數:
asyncdefindex(request, page=1):
為什麼必須定義這個參數,它從哪來?它有什麼作用,下面我將一一為你解答。
如果你在客戶端請求的時候,順手在視圖函數裡面列印下參數,會有如下輸出:
看終端的輸出可以了解到參數實際上是一個名為的實例對象,每當服務端接收到一個請求,Sanic的函數必定會接收一個實例對象,這個實例對象包含了一系列請求信息。
前面說到,每個URL對應一個視圖函數,而Sanic的接下來會將接收的實例對象作為參數傳給URL對應的視圖函數,也就是上面的參數,這樣一來,就必須定義來接收實例對象,其中包含的一些請求信息對視圖函數來說非常重要,目前對象提供了以下屬性:
上面只是列出了一部分屬性,如果你想了解更多,可查看request.py源碼文件了解。
為了可以實際使用下,我們可以再加一個需求,比如增加一個請求的介面,如果請求不設置參數的值,則默認返回一條資訊新聞,如果設置了參數,則該介面返回的新聞數量由參數值決定,參數最大值為10:
@app.route("/json")async def index_json(request):""" 默認返回一條資訊,最多十條 """nums = request.args.get("nums",1) # 獲取數據 all_news = await get_news() try: return json(random.sample(all_news, int(nums))) except ValueError: return json(all_news)
運行此服務:
python run news.py
此時視圖函數就可以根據接受的參數來返回對應數量的新聞,訪問,效果如下:
8.響應
不論哪個Web框架,都是需要構建響應對象的,Sanic自然也不例外,它用的是來構建響應對象,像上面的代碼中可以看到:
這表示我們目前構建的資訊閱讀服務,分別返回了格式為以及的響應對象,除了這兩種格式,Sanic還提供了下面幾種格式:
更多屬性請看response.py,我們可以根據實際需求來構建響應對象,最後再返回給客戶端。
9.繼續深入
不要以為現在編寫的資訊服務已經很完善了,其實還有許多問題需要我們解決,比如訪問這個URL會返回:
Error: Requested URL /html not found
服務程序為什麼會拋出這個錯誤?因為程序中並路由沒有註冊,並且沒有進行錯誤捕捉(比如此時的404),解決這個問題也很方便,比如把這個錯誤全部跳轉到首頁,代碼如下:
@app.exception(NotFound)defignore_404s(request, exception):returnredirect("/")
此時訪問一些沒有註冊於路由的URL,比如此時的都會自動跳轉到。
現在,我們已經用Sanic編寫了一個簡單的資訊閱讀服務,在編寫的過程中使用了路由、數據請求、處理以及響應對象,這些基礎知識足夠你編寫一些基本的服務,但這還遠遠不夠,比如模板引、引入靜態文件等,這些都等著我們在實踐中繼續深入了解。
10.總結
本章介紹了Sanic的安裝以及基本的使用,目標是希望諸位可以迅速的了解並掌握Sanic的基本使用方法,並為閱讀接下來的章節打一下基礎。
文檔以及代碼:
TAG:老胡的儲物櫃 |