(2018-05-30.Python從Zero到One)8、(Tornado)異步與WebSockets__1.7.2 Tornado異步

7.2 Tornado異步

因?yàn)閑poll主要是用來解決網(wǎng)絡(luò)IO的并發(fā)問題秃臣,所以Tornado的異步編程也主要體現(xiàn)在網(wǎng)絡(luò)IO的異步上假夺,即異步Web請(qǐng)求。

1. tornado.httpclient.AsyncHTTPClient

Tornado提供了一個(gè)異步Web請(qǐng)求客戶端tornado.httpclient.AsyncHTTPClient用來進(jìn)行異步Web請(qǐng)求斋攀。

fetch(request, callback=None)

用于執(zhí)行一個(gè)web請(qǐng)求request已卷,并異步返回一個(gè)tornado.httpclient.HTTPResponse響應(yīng)。

request可以是一個(gè)url淳蔼,也可以是一個(gè)tornado.httpclient.HTTPRequest對(duì)象侧蘸。如果是url,fetch會(huì)自己構(gòu)造一個(gè)HTTPRequest對(duì)象鹉梨。

HTTPRequest

HTTP請(qǐng)求類讳癌,HTTPRequest的構(gòu)造函數(shù)可以接收眾多構(gòu)造參數(shù),最常用的如下:

  • url (string) – 要訪問的url存皂,此參數(shù)必傳晌坤,除此之外均為可選參數(shù)
  • method (string) – HTTP訪問方式,如“GET”或“POST”旦袋,默認(rèn)為GET方式
  • headers (HTTPHeaders or dict) – 附加的HTTP協(xié)議頭
  • body – HTTP請(qǐng)求的請(qǐng)求體

HTTPResponse

HTTP響應(yīng)類骤菠,其常用屬性如下:

  • code: HTTP狀態(tài)碼,如 200 或 404
  • reason: 狀態(tài)碼描述信息
  • body: 響應(yīng)體字符串
  • error: 異常(可有可無)

2. 測(cè)試接口

新浪IP地址庫(kù)

接口說明

1.請(qǐng)求接口(GET):

[http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=ip地址字串]

2.響應(yīng)信息:

(json格式的)國(guó)家 疤孕、噬毯酢(自治區(qū)或直轄市)、市(縣)祭阀、運(yùn)營(yíng)商

3.返回?cái)?shù)據(jù)格式:

{"ret":1,"start":-1,"end":-1,"country":"\u4e2d\u56fd","province":"\u5317\u4eac","city":"\u5317\u4eac","district":"","isp":"","type":"","desc":""}

3. 回調(diào)異步

class IndexHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous  # 不關(guān)閉連接鹉戚,也不發(fā)送響應(yīng)
    def get(self):
        http = tornado.httpclient.AsyncHTTPClient()
        http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=14.130.112.24",
                   callback=self.on_response)

    def on_response(self, response):
        if response.error:
            self.send_error(500)
        else:
            data = json.loads(response.body)
            if 1 == data["ret"]:
                self.write(u"國(guó)家:%s 省份: %s 城市: %s" % (data["country"], data["province"], data["city"]))
            else:
                self.write("查詢IP信息錯(cuò)誤")
        self.finish() # 發(fā)送響應(yīng)信息,結(jié)束請(qǐng)求處理

tornado.web.asynchronous

此裝飾器用于回調(diào)形式的異步方法专控,并且應(yīng)該僅用于HTTP的方法上(如get抹凳、post等)。

此裝飾器不會(huì)讓被裝飾的方法變?yōu)楫惒讲裙伲皇歉嬖V框架被裝飾的方法是異步的却桶,當(dāng)方法返回時(shí)響應(yīng)尚未完成。只有在request handler調(diào)用了finish方法后蔗牡,才會(huì)結(jié)束本次請(qǐng)求處理颖系,發(fā)送響應(yīng)。

不帶此裝飾器的請(qǐng)求在get辩越、post等方法返回時(shí)自動(dòng)完成結(jié)束請(qǐng)求處理嘁扼。

4. 協(xié)程異步

在上一節(jié)中我們自己封裝的裝飾器get_coroutine在Tornado中對(duì)應(yīng)的是tornado.gen.coroutine。

class IndexHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        http = tornado.httpclient.AsyncHTTPClient()
        response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=14.130.112.24")
        if response.error:
            self.send_error(500)
        else:
            data = json.loads(response.body)
            if 1 == data["ret"]:
                self.write(u"國(guó)家:%s 省份: %s 城市: %s" % (data["country"], data["province"], data["city"]))
            else:
                self.write("查詢IP信息錯(cuò)誤")

也可以將異步Web請(qǐng)求單獨(dú)出來:

class IndexHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        rep = yield self.get_ip_info("14.130.112.24")
        if 1 == rep["ret"]:
            self.write(u"國(guó)家:%s 省份: %s 城市: %s" % (rep["country"], rep["province"], rep["city"]))
        else:
            self.write("查詢IP信息錯(cuò)誤")

    @tornado.gen.coroutine
    def get_ip_info(self, ip):
        http = tornado.httpclient.AsyncHTTPClient()
        response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=" + ip)
        if response.error:
            rep = {"ret:0"}
        else:
            rep = json.loads(response.body)
        raise tornado.gen.Return(rep)  # 此處需要注意

代碼中我們需要注意的地方是get_ip_info返回值的方式黔攒,在python 2中趁啸,使用了yield的生成器可以使用不返回任何值的return强缘,但不能return value,因此Tornado為我們封裝了用于在生成器中返回值的特殊異常tornado.gen.Return不傅,并用raise來返回此返回值旅掂。

并行協(xié)程

Tornado可以同時(shí)執(zhí)行多個(gè)異步,并發(fā)的異步可以使用列表或字典访娶,如下:

class IndexHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        ips = ["14.130.112.24",
            "15.130.112.24",
            "16.130.112.24",
            "17.130.112.24"]
        rep1, rep2 = yield [self.get_ip_info(ips[0]), self.get_ip_info(ips[1])]
        rep34_dict = yield dict(rep3=self.get_ip_info(ips[2]), rep4=self.get_ip_info(ips[3]))
        self.write_response(ips[0], rep1) 
        self.write_response(ips[1], rep2) 
        self.write_response(ips[2], rep34_dict['rep3']) 
        self.write_response(ips[3], rep34_dict['rep4']) 

    def write_response(self, ip, response):
        self.write(ip) 
        self.write(":<br/>") 
        if 1 == response["ret"]:
            self.write(u"國(guó)家:%s 省份: %s 城市: %s<br/>" % (response["country"], response["province"], response["city"]))
        else:
            self.write("查詢IP信息錯(cuò)誤<br/>")

    @tornado.gen.coroutine
    def get_ip_info(self, ip):
        http = tornado.httpclient.AsyncHTTPClient()
        response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=" + ip)
        if response.error:
            rep = {"ret:1"}
        else:
            rep = json.loads(response.body)
        raise tornado.gen.Return(rep)

5. 關(guān)于數(shù)據(jù)庫(kù)的異步說明

網(wǎng)站基本都會(huì)有數(shù)據(jù)庫(kù)操作商虐,而Tornado是單線程的,這意味著如果數(shù)據(jù)庫(kù)查詢返回過慢崖疤,整個(gè)服務(wù)器響應(yīng)會(huì)被堵塞秘车。

數(shù)據(jù)庫(kù)查詢,實(shí)質(zhì)上也是遠(yuǎn)程的網(wǎng)絡(luò)調(diào)用劫哼;理想情況下叮趴,是將這些操作也封裝成為異步的;但Tornado對(duì)此并沒有提供任何支持权烧。

這是Tornado的設(shè)計(jì)眯亦,而不是缺陷。

一個(gè)系統(tǒng)豪嚎,要滿足高流量搔驼;是必須解決數(shù)據(jù)庫(kù)查詢速度問題的!

數(shù)據(jù)庫(kù)若存在查詢性能問題侈询,整個(gè)系統(tǒng)無論如何優(yōu)化舌涨,數(shù)據(jù)庫(kù)都會(huì)是瓶頸,拖慢整個(gè)系統(tǒng)扔字!

異步并不能從本質(zhì)上提到系統(tǒng)的性能囊嘉;它僅僅是避免多余的網(wǎng)絡(luò)響應(yīng)等待,以及切換線程的CPU耗費(fèi)革为。

如果數(shù)據(jù)庫(kù)查詢響應(yīng)太慢扭粱,需要解決的是數(shù)據(jù)庫(kù)的性能問題;而不是調(diào)用數(shù)據(jù)庫(kù)的前端Web應(yīng)用震檩。

對(duì)于實(shí)時(shí)返回的數(shù)據(jù)查詢琢蛤,理想情況下需要確保所有數(shù)據(jù)都在內(nèi)存中,數(shù)據(jù)庫(kù)硬盤IO應(yīng)該為0抛虏;這樣的查詢才能足夠快博其;而如果數(shù)據(jù)庫(kù)查詢足夠快,那么前端web應(yīng)用也就無將數(shù)據(jù)查詢封裝為異步的必要迂猴。

就算是使用協(xié)程慕淡,異步程序?qū)τ谕匠绦蚴冀K還是會(huì)提高復(fù)雜性;需要衡量的是處理這些額外復(fù)雜性是否值得沸毁。

如果后端有查詢實(shí)在是太慢峰髓,無法繞過傻寂,Tornaod的建議是將這些查詢?cè)诤蠖朔庋b獨(dú)立封裝成為HTTP接口,然后使用Tornado內(nèi)置的異步HTTP客戶端進(jìn)行調(diào)用携兵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疾掰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子徐紧,更是在濱河造成了極大的恐慌个绍,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浪汪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凛虽,警方通過查閱死者的電腦和手機(jī)死遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凯旋,“玉大人呀潭,你說我怎么就攤上這事≈练牵” “怎么了钠署?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)荒椭。 經(jīng)常有香客問我谐鼎,道長(zhǎng),這世上最難降的妖魔是什么趣惠? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任狸棍,我火速辦了婚禮,結(jié)果婚禮上味悄,老公的妹妹穿的比我還像新娘草戈。我一直安慰自己,他們只是感情好侍瑟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布唐片。 她就那樣靜靜地躺著,像睡著了一般涨颜。 火紅的嫁衣襯著肌膚如雪费韭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天咐低,我揣著相機(jī)與錄音揽思,去河邊找鬼。 笑死见擦,一個(gè)胖子當(dāng)著我的面吹牛钉汗,可吹牛的內(nèi)容都是我干的羹令。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼损痰,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼福侈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卢未,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤肪凛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辽社,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伟墙,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年滴铅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了戳葵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汉匙,死狀恐怖拱烁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情噩翠,我是刑警寧澤戏自,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站伤锚,受9級(jí)特大地震影響擅笔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜见芹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一剂娄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玄呛,春花似錦阅懦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惕它,卻和暖如春怕午,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淹魄。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工郁惜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甲锡。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓兆蕉,卻偏偏與公主長(zhǎng)得像羽戒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虎韵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容