在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 用 tornado 做網(wǎng)站 (7)
標準庫 (4)
如何成為 Python 高手
標準庫 (6)
標準庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準備
函數(shù)練習
標準庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標準庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標準庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標準庫 (2)
列表(2)
元組

用 tornado 做網(wǎng)站 (7)

到上一節(jié)結束,其實讀者已經(jīng)能夠做一個網(wǎng)站了,但是,僅僅用前面的技術來做的網(wǎng)站,僅能算一個小網(wǎng)站,在《為做網(wǎng)站而準備》中,說明之所以選 tornado,就是因為它能夠解決 c10k 問題,即能夠實現(xiàn)大用戶量訪問。

要實現(xiàn)大用戶量訪問,必須要做的就是:異步。除非你是很土的土豪。

相關概念

同步和異步

有不少資料對這兩個概念做了不同角度和層面的解釋。在我來看,一個最典型的例子就是打電話和發(fā)短信。

  • 打電話就是同步。張三給李四打電話,張三說:“是李四嗎?”。當這個信息被張三發(fā)出,提交給李四,就等待李四的響應(一般會聽到“是”,或者“不是”),只有得到了李四返回的信息之后,才能進行后續(xù)的信息傳送。
  • 發(fā)短信是異步。張三給李四發(fā)短信,編輯了一句話“今晚一起看老齊的零基礎學 Python”,發(fā)送給李四。李四或許馬上回復,或許過一段時間,這段時間多長也不定,才回復。總之,李四不管什么時候回復,張三會以聽到短信鈴聲為提示查看短信。

以上方式理解“同步”和“異步”不是很精準,有些地方或有牽強。要嚴格理解,需要用嚴格一點的定義表述(以下表述參照了知乎上的回答):

同步和異步關注的是消息通信機制 (synchronous communication/ asynchronous communication)

所謂同步,就是在發(fā)出一個“調用”時,在沒有得到結果之前,該“調用”就不返回。但是一旦調用返回,就得到返回值了。 換句話說,就是由“調用者”主動等待這個“調用”的結果。

而異步則是相反,“調用”在發(fā)出之后,這個調用就直接返回了,所以沒有返回結果。換句話說,當一個異步過程調用發(fā)出后,調用者不會立刻得到結果。而是在“調用”發(fā)出后,“被調用者”通過狀態(tài)、通知來通知調用者,或通過回調函數(shù)處理這個調用。

可能還是前面的打電話和發(fā)短信更好理解。

阻塞和非阻塞

“阻塞和非阻塞”與“同步和異步”常常被換為一談,其實它們之間還是有差別的。如果按照一個“差不多”先生的思維方法,你也可以不那么深究它們之間的學理上的差距,反正在你的程序中,會使用就可以了。不過,必要的嚴謹還是需要的,特別是我寫這個教程,要裝扮的讓別人看來自己懂,于是就再引用知乎上的說明(我個人認為,別人已經(jīng)做的挺好的東西,就別重復勞動了,“拿來主義”,也不錯。或許你說我抄襲和山寨,但是我明確告訴你來源了):

阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態(tài).

阻塞調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之后才會返回。非阻塞調用指在不能立刻得到結果之前,該調用不會阻塞當前線程。

按照這個說明,發(fā)短信就是顯然的非阻塞,發(fā)出去一條短信之后,你利用手機還可以干別的,乃至于再發(fā)一條“老齊的課程沒意思,還是看 PHP 刺激”也是可以的。

關于這兩組基本概念的辨析,不是本教程的重點,讀者可以參閱這篇文章:http://www.cppblog.com/converse/archive/2009/05/13/82879.html,文章作者做了細致入微的辨析。

tornado 的同步

此前,在 tornado 基礎上已經(jīng)完成的 web,就是同步的、阻塞的。為了更明顯的感受這點,不妨這樣試一試。

在 handlers 文件夾中建立一個文件,命名為 sleep.py

#!/usr/bin/env python
# coding=utf-8

from base import BaseHandler

import time

class SleepHandler(BaseHandler):
    def get(self):
        time.sleep(17)
        self.render("sleep.html")

class SeeHandler(BaseHandler):
    def get(self):
        self.render("see.html")

其它的事情,如果讀者對我在《用 tornado 做網(wǎng)站 (1)》中所講述的網(wǎng)站框架熟悉,應該知道如何做了,不熟悉,請回頭復習。

sleep.html 和 see.html 是兩個簡單的模板,內容可以自己寫。別忘記修改 url.py 中的目錄。

然后的測試稍微復雜一點點,就是打開瀏覽器之后,打開兩個標簽,分別在兩個標簽中輸入 localhost:8000/sleep(記為標簽 1)和 localhost:8000/see(記為標簽 2),注意我用的是 8000 端口。輸入之后先不要點擊回車去訪問。做好準備,記住切換標簽可以用“ctrl-tab”組合鍵。

  1. 執(zhí)行標簽 1,讓它訪問網(wǎng)站;
  2. 馬上切換到標簽 2,訪問網(wǎng)址。
  3. 注意觀察,兩個標簽頁面,是不是都在顯示正在訪問,請等待。
  4. 當標簽 1 不呈現(xiàn)等待提示(比如一個正在轉的圓圈)時,標簽 2 的表現(xiàn)如何?幾乎同時也訪問成功了。

建議讀者修改 sleep.py 中的 time.sleep(17) 這個值,多試試。很好玩的吧。

當然,這是比較笨拙的方法,本來是可以通過測試工具完成上述操作比較的。怎奈要用別的工具,還要進行介紹,又多了一個分散精力的東西,故用如此笨拙的方法,權當有一個體會。

異步設置

tornado 本來就是一個異步的服務框架,體現(xiàn)在 tornado 的服務器和客戶端的網(wǎng)絡交互的異步上,起作用的是 tornado.ioloop.IOLoop。但是如果的客戶端請求服務器之后,在執(zhí)行某個方法的時候,比如上面的代碼中執(zhí)行 get() 方法的時候,遇到了 time.sleep(17) 這個需要執(zhí)行時間比較長的操作,耗費時間,就會使整個 tornado 服務器的性能受限了。

為了解決這個問題,tornado 提供了一套異步機制,就是異步裝飾器 @tornado.web.asynchronous

#!/usr/bin/env Python
# coding=utf-8

import tornado.web
from base import BaseHandler

import time

class SleepHandler(BaseHandler):
    @tornado.web.asynchronous
    def get(self):
        tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 17, callback=self.on_response)
    def on_response(self):
        self.render("sleep.html")
        self.finish()

將 sleep.py 的代碼如上述一樣改造,即在 get() 方法前面增加了裝飾器 @tornado.web.asynchronous,它的作用在于將 tornado 服務器本身默認的設置_auto_fininsh 值修改為 false。如果不用這個裝飾器,客戶端訪問服務器的 get() 方法并得到返回值之后,兩只之間的連接就斷開了,但是用了 @tornado.web.asynchronous 之后,這個連接就不關閉,直到執(zhí)行了 self.finish() 才關閉這個連接。

tornado.ioloop.IOLoop.instance().add_timeout() 也是一個實現(xiàn)異步的函數(shù),time.time()+17 是給前面函數(shù)提供一個參數(shù),這樣實現(xiàn)了相當于 time.sleep(17) 的功能,不過,還沒有完成,當這個操作完成之后,就執(zhí)行回調函數(shù) on_response() 中的 self.render("sleep.html"),并關閉連接 self.finish()。

過程清楚了。所謂異步,就是要解決原來的 time.sleep(17) 造成的服務器處理時間長,性能下降的問題。解決方法如上描述。

讀者看這個代碼,或許感覺有點不是很舒服。如果有這么一點感覺,是正常的。因為它里面除了裝飾器之外,用到了一個回調函數(shù),它讓代碼的邏輯不是平鋪下去,而是被分割為了兩段。第一段是 tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 17, callback=self.on_response),用callback=self.on_response 來使用回調函數(shù),并沒有如同改造之前直接 self.render("sleep.html");第二段是回調函數(shù) on_response(self),要在這個函數(shù)里面執(zhí)行self.render("sleep.html"),并且以self.finish()`結尾以關閉連接。

這還是執(zhí)行簡單邏輯,如果復雜了,不斷地要進行“回調”,無法讓邏輯順利延續(xù),那面會“眩暈”了。這種現(xiàn)象被業(yè)界成為“代碼邏輯拆分”,打破了原有邏輯的順序性。為了讓代碼邏輯不至于被拆分的七零八落,于是就出現(xiàn)了另外一種常用的方法:

#!/usr/bin/env Python
# coding=utf-8

import tornado.web
import tornado.gen
from base import BaseHandler

import time

class SleepHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 17)
        #yield tornado.gen.sleep(17)
        self.render("sleep.html")

從整體上看,這段代碼避免了回調函數(shù),看著順利多了。

再看細節(jié)部分。

首先使用的是 @tornado.gen.coroutine 裝飾器,所以要在前面有 import tornado.gen。跟這個裝飾器類似的是 @tornado.gen.engine 裝飾器,兩者功能類似,有一點細微差別。請閱讀官方對此的解釋

This decorator(指 engine) is similar to coroutine, except it does not return a Future and the callback argument is not treated specially.

@tornado.gen.engine 是古時候用的,現(xiàn)在我們都使用 @tornado.gen.corroutine 了,這個是在 tornado 3.0 以后開始。在網(wǎng)上查閱資料的時候,會遇到一些使用 @tornado.gen.engine 的,但是在你使用或者借鑒代碼的時候,就勇敢地將其修改為 @tornado.gen.coroutine 好了。有了這個裝飾器,就能夠控制下面的生成器的流程了。

然后就看到 get() 方法里面的 yield 了,這是一個生成器(參閱本教程《生成器》)。yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 17) 的執(zhí)行過程,應該先看括號里面,跟前面的一樣,是來替代 time.sleep(17) 的,然后是 tornado.gen.Task() 方法,其作用是“Adapts a callback-based asynchronous function for use in coroutines.”(由于怕翻譯后遺漏信息,引用原文)。返回后,最后使用 yield 得到了一個生成器,先把流程掛起,等完全完畢,再喚醒繼續(xù)執(zhí)行。要提醒讀者,生成器都是異步的。

其實,上面啰嗦一對,可以用代碼中注釋了的一句話來代替 yield tornado.gen.sleep(17),之所以擴所,就是為了順便看到 tornado.gen.Task() 方法,因為如果讀者在看古老的代碼時候,會遇到。但是,后面你寫的時候,就不要那么啰嗦了,請用 yield tornado.gen.sleep()。

至此,基本上對 tornado 的異步設置有了概覽,不過,上面的程序在實際中沒有什么價值。在工程中,要讓 tornado 網(wǎng)站真正異步起來,還要做很多事情,不僅僅是如上面的設置,因為很多東西,其實都不是異步的。

實踐中的異步

以下各項同步(阻塞)的,如果在 tornado 中按照之前的方式只用它們,就是把 tornado 的非阻塞、異步優(yōu)勢削減了。

  • 數(shù)據(jù)庫的所有操作,不管你的數(shù)據(jù)是 SQL 還是 noSQL,connect、insert、update 等
  • 文件操作,打開,讀取,寫入等
  • time.sleep,在前面舉例中已經(jīng)看到了
  • smtplib,發(fā)郵件的操作
  • 一些網(wǎng)絡操作,比如 tornado 的 httpclient 以及 pycurl 等

除了以上,或許在編程實踐中還會遇到其他的同步、阻塞實踐。僅僅就上面幾項,就是編程實踐中經(jīng)常會遇到的,怎么解決?

聰明的大牛程序員幫我們做了擴展模塊,專門用來實現(xiàn)異步/非阻塞的。

  • 在數(shù)據(jù)庫方面,由于種類繁多,不能一一說明,比如 mysql,可以使用adb模塊來實現(xiàn) python 的異步 mysql 庫;對于 mongodb 數(shù)據(jù)庫,有一個非常優(yōu)秀的模塊,專門用于在 tornado 和 mongodb 上實現(xiàn)異步操作,它就是 motor。特別貼出它的 logo,我喜歡。官方網(wǎng)站:http://motor.readthedocs.org/en/stable/上的安裝和使用方法都很詳細。

http://wiki.jikexueyuan.com/project/start-learning-python/images/30901.png" alt="" />

  • 文件操作方面也沒有替代模塊,只能盡量控制好 IO,或者使用內存型(Redis)及文檔型(MongoDB)數(shù)據(jù)庫。
  • time.sleep() 在 tornado 中有替代:tornado.gen.sleep() 或者 tornado.ioloop.IOLoop.instance().add_timeout,這在前面代碼已經(jīng)顯示了。
  • smtp 發(fā)送郵件,推薦改為 tornado-smtp-client。
  • 對于網(wǎng)絡操作,要使用 tornado.httpclient.AsyncHTTPClient。

其它的解決方法,只能看到問題具體說了,甚至沒有很好的解決方法。不過,這里有一個列表,列出了足夠多的庫,供使用者選擇:Async Client Libraries built on tornado.ioloop,同時這個頁面里面還有很多別的鏈接,都是很好的資源,建議讀者多看看。

教程到這里,讀者是不是要思考一個問題,既然對于 mongodb 有專門的 motor 庫來實現(xiàn)異步,前面對于 tornado 的異步,不管是哪個裝飾器,都感覺麻煩,有沒有專門的庫來實現(xiàn)這種異步呢?這不是異想天開,還真有。也應該有,因為這才體現(xiàn)python的特點。比如greenlet-tornado,就是一個不錯的庫。讀者可以瀏覽官方網(wǎng)站深入了解(為什么對 mysql 那么不積極呢?按理說應該出來好多支持 mysql 異步的庫才對)。

必須聲明,前面演示如何在 tornado 中設置異步的代碼,僅僅是演示理解設置方法。在工程實踐中,那個代碼的意義不到。為此,應該有一個近似于實踐的代碼示例。是的,的確應該有。當我正要寫這樣的代碼時候,在網(wǎng)上發(fā)現(xiàn)一篇文章,這篇文章阻止了我寫,因為我要寫的那篇文章的作者早就寫好了,而且我認為表述非常到位,示例也詳細。所以,我不得不放棄,轉而推薦給讀者這篇好文章:

舉例:http://emptysqua.re/blog/refactoring-tornado-coroutines/


總目錄   |   上節(jié):用 tornado 做網(wǎng)站 (6)   |   下節(jié):為計算做準備

如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。

上一篇:電子表格下一篇:字典(2)