《Introduction to Tornado》中文翻譯計劃——第五章:異步Web服務

http://www.pythoner.com/294.htmljavascript

本文爲《Introduction to Tornado》中文翻譯,將在https://github.com/alioth310/itt2zh上面持續更新,本文內容可能不是最新狀態,請在GitHub上得到最新版本。html

本文也可在http://demo.pythoner.com/itt2zh上進行格式化的預覽。前端

第五章:異步Web服務java

到目前爲止,咱們已經看到了許多使Tornado成爲一個Web應用強有力框架的功能。它的簡單性、易用性和便捷性使其有足夠的理由成爲許多Web項目的不錯的選擇。然而,Tornado受到最多關注的功能是其異步取得和提供內容的能力,它有着很好的理由:它使得處理非阻塞請求更容易,最終致使更高效的處理以及更好的可擴展性。在本章中,咱們將看到Tornado異步請求的基礎,以及一些推送技術,這種技術可使你使用更少的資源來提供更多的請求以編寫更簡單的Web應用。
python

5.1 異步Web請求

大部分Web應用(包括咱們以前的例子)都是阻塞性質的,也就是說當一個請求被處理時,這個進程就會被掛起直至請求完成。在大多數狀況下,Tornado處理的Web請求完成得足夠快使得這個問題並不須要被關注。然而,對於那些須要一些時間來完成的操做(像大數據庫的請求或外部API),這意味着應用程序被有效的鎖定直至處理結束,很明顯這在可擴展性上出現了問題。jquery

不過,Tornado給了咱們更好的方法來處理這種狀況。應用程序在等待第一個處理完成的過程當中,讓I/O循環打開以便服務於其餘客戶端,直處處理完成時啓動一個請求並給予反饋,而再也不是等待請求完成的過程當中掛起進程。git

爲了實現Tornado的異步功能,咱們構建一個向Twotter搜索API發送HTTP請求的簡單Web應用。這個Web應用有一個參數q做爲查詢字符串,並肯定多久會出現一條符合搜索條件的推文被髮布在Twitter上(」每秒推數」)。肯定這個數值的方法很是粗糙,但足以達到例子的目的。圖5-1展現了這個應用的界面。程序員

Figure5-1
圖5-1 異步HTTP示例:推率github

咱們將展現這個應用的三個不一樣版本:首先,是一個使用同步HTTP請求的版本,而後是一個使用帶有回調函數的Tornado異步HTTP客戶端版本。最後,咱們將展現如何使用Tornado 2.1版本新增的gen模塊來使異步HTTP請求更加清晰和易實現。爲了理解這些例子,你不須要成爲關於Twitter搜索API的專家,但必定的熟悉不會有害。你能夠在https://dev.twitter.com/docs/api/1/get/search閱讀關於搜索API的開發者文檔。web

5.1.1 從同步開始

代碼清單5-1包含咱們的推率計算器的同步版本的代碼。記住咱們在頂部導入了Tornado的httpclient模塊:咱們將使用這個模塊的HTTPClient類來執行HTTP請求。以後,咱們將使用這個模塊的AsyncHTTPClient。

 

 

這個程序的結構如今對你而言應該已經很熟悉了:咱們有一個RequestHandler類和一個處理到應用根路徑請求的IndexHandler。在IndexHandler的get方法中,咱們從查詢字符串中抓取參數q,而後用它執行一個到Twitter搜索API的請求。下面是最相關的一部分代碼:

 

 

這裏咱們實例化了一個Tornado的HTTPClient類,而後調用結果對象的fetch方法。fetch方法的同步版本使用要獲取的URL做爲參數。這裏,咱們構建一個URL來抓取Twitter搜索API的相關搜索結果(rpp參數指定咱們想得到搜索結果首頁的100個推文,而result_type參數指定咱們只想得到匹配搜索的最近推文)。fetch方法會返回一個HTTPResponse對象,其 body屬性包含咱們從遠端URL獲取的任何數據。Twitter將返回一個JSON格式的結果,因此咱們可使用Python的json模塊來從結果中建立一個Python數據結構。

tipfetch方法返回的HTTPResponse對象容許你訪問HTTP響應的任何部分,不僅是body。能夠在官方文檔[1]閱讀更多相關信息。

處理函數的其他部分關注的是計算每秒推文數。咱們使用搜索結果中最舊推文與最新推文時間戳之差來肯定搜索覆蓋的時間,而後使用這個數值除以搜索取得的推文數來得到咱們的最終結果。最後,咱們編寫了一個擁有這個結果的簡單HTML頁面給瀏覽器。

5.1.2 阻塞的困擾

到目前爲止,咱們已經編寫了 一個請求Twitter API並向瀏覽器返回結果的簡單Tornado應用。儘管應用程序自己響應至關快,可是向Twitter發送請求到得到返回的搜索數據之間有至關大的滯後。在同步(到目前爲止,咱們假定爲單線程)應用,這意味着同時只能提供一個請求。因此,若是你的應用涉及一個2秒的API請求,你將每間隔一秒才能提供(最多!)一個請求。這並非你所稱的高可擴展性應用,即使擴展到多線程和/或多服務器 。

爲了更具體的看出這個問題,咱們對剛編寫的例子進行基準測試。你可使用任何基準測試工具來驗證這個應用的性能,不過在這個例子中咱們使用優秀的Siege utility工具進行測試。它能夠這樣使用:

 

 

在這個例子中,Siege對咱們的應用在10秒內執行大約10個併發請求,輸出結果如圖5-2所示。咱們能夠很容易看出,這裏的問題是不管每一個請求自身返回多麼快,API往返都會以致於產生足夠大的滯後,由於進程直到請求完成而且數據被處理前都一直處於強制掛起狀態。當一兩個請求時這還不是一個問題,但達到100個(甚至10個)用戶時,這意味着總體變慢。

Figure5-2
圖5-2 同步推率獲取

此時,不到10秒時間10個類似用戶的平均響應時間達到了1.99秒,共計29次。請記住,這個例子只提供了一個很是簡單的網頁。若是你要添加其餘Web服務或數據庫的調用的話,結果會更糟糕。這種代碼若是被 用到網站上,即使是中等強度的流量都會致使請求增加緩慢,甚至發生超時或失敗。

5.1.3 基礎異步調用

幸運的是,Tornado包含一個AsyncHTTPClient類,能夠執行異步HTTP請求。它和代碼清單5-1的同步客戶端實現有必定的類似性,除了一些咱們將要討論的重要區別。代碼清單5-2是其源代碼。

 

 

AsyncHTTPClient的fetch方法並不返回調用的結果。取而代之的是它指定了一個callback參數;你指定的方法或函數將在HTTP請求完成時被調用,並使用HTTPResponse做爲其參數。

 

 

在這個例子中,咱們指定on_response方法做爲回調函數。咱們以前使用指望的輸出轉化Twitter搜索API請求到網頁中的全部邏輯被搬到了on_response函數中。還須要注意的是@tornado.web.asynchronous裝飾器的使用(在get方法的定義以前)以及在回調方法結尾處調用的self.finish()。咱們稍後將簡要的討論他們的細節。

這個版本的應用擁有和以前同步版本相同的外觀,但其性能更加優越。有多好呢?讓咱們看看基準測試的結果吧。

正如你在圖5-3中所看到的,咱們從同步版本的每秒3.20個事務提高到了12.59,在相同的時間內總共提供了118次請求。這真是一個很是大的改善!正如你所想象的,當擴展到更多用戶和更長時間時,它將可以提供更多鏈接,而且不會遇到同步版本遭受的變慢的問題。

Figure5-3
圖5-3 異步推率獲取

5.1.4 異步裝飾器和finish方法

Tornado默認在函數處理返回時關閉客戶端的鏈接。在一般狀況下,這正是你想要的。可是當咱們處理一個須要回調函數的異步請求時,咱們須要鏈接保持開啓狀態直到回調函數執行完畢。你能夠在你想改變其行爲的方法上面使用@tornado.web.asynchronous裝飾器來告訴Tornado保持鏈接開啓,正如咱們在異步版本的推率例子中IndexHandler的get方法中所作的。下面是相關的代碼片斷:

 

 

記住當你使用@tornado.web.asynchonous裝飾器時,Tornado永遠不會本身關閉鏈接。你必須在你的RequestHandler對象中調用finish方法來顯式地告訴Tornado關閉鏈接。(不然,請求將可能掛起,瀏覽器可能不會顯示咱們已經發送給客戶端的數據。)在前面的異步示例中,咱們在on_response函數的write後面調用了finish方法:

 

 

5.1.5 異步生成器

如今,咱們的推率程序的異步版本運轉的不錯而且性能也很好。不幸的是,它有點麻煩:爲了處理請求 ,咱們不得不把咱們的代碼分割成兩個不一樣的方法。當咱們有兩個或更多的異步請求要執行的時候,編碼和維護都顯得很是困難,每一個都依賴於前面的調用:不久你就會發現本身調用了一個回調函數的回調函數的回調函數。下面就是一個構想出來的(但不是不可能的)例子:

 

 

幸運的是,Tornado 2.1版本引入了tornado.gen模塊,能夠提供一個更整潔的方式來執行異步請求。代碼清單5-3就是使用了tornado.gen版本的推率應用源代碼。讓咱們先來看一下,而後討論它是如何工做的。

 

 

正如你所看到的,這個代碼和前面兩個版本的代碼很是類似。主要的不一樣點是咱們如何調用Asynchronous對象的fetch方法。下面是相關的代碼部分:

 

 

咱們使用Python的yield關鍵字以及tornado.gen.Task對象的一個實例,將咱們想要的調用和傳給該調用函數的參數傳遞給那個函數。這裏,yield的使用返回程序對Tornado的控制,容許在HTTP請求進行中執行其餘任務。當HTTP請求完成時,RequestHandler方法在其中止的地方恢復。這種構建的美在於它在請求處理程序中返回HTTP響應,而不是回調函數中。所以,代碼更易理解:全部請求相關的邏輯位於同一個位置。而HTTP請求依然是異步執行的,因此咱們使用tornado.gen能夠達到和使用回調函數的異步請求版本相同的性能,正如咱們在圖5-4中所看到的那樣。

Figure5-4
圖5-4 使用tornado.gen的異步推率獲取

記住@tornado.gen.engine裝飾器的使用須要恰好在get方法的定義以前;這將提醒Tornado這個方法將使用tornado.gen.Task類。tornado.gen模塊還喲一些其餘類和函數能夠方便Tornado的異步編程。查閱一下文檔[1]是很是值得的。

使一切異步

在本章中咱們使用了Tornado的異步HTTP客戶端做爲如何執行異步任務的實現。其餘開發者也編寫了針對其餘任務的異步客戶端庫。志願者們在Tornado wiki上維護了一個關於這些庫的至關完整的列表。

一個重要的例子是bit.ly的asyncmongo,它能夠異步的調用MongoDB服務器。這個庫是咱們的一個很是不錯的選擇,由於它是專門給Tornado開發者開發提供異步數據庫訪問的,不過對於使用其餘數據庫的用戶而言,在這裏也能夠找到不錯的異步數據存儲庫的選擇。

5.1.6 異步操做總結

正如咱們在前面的例子中所看到的,Tornado異步Web發服務不只容易實現也在實踐中有着不容小覷的能力。使用異步處理可讓咱們的應用在長時間的API和數據庫請求中免受阻塞之苦,最終更快地提供更多請求。儘管不是全部的處理都能從異步中受益–而且實際上嘗試整個程序非阻塞會迅速使事情變得複雜–但Tornado的非阻塞功能能夠很是方便的建立依賴於緩慢查詢或外部服務的Web應用。

不過,值得注意的是,這些例子都很是的作做。若是你正在設計一個任何規模下帶有該功能的應用,你可能但願客戶端瀏覽器來執行Twitter搜索請求(使用JavaScript),而讓Web服務器轉向提供其餘請求。在大多數狀況下,你至少但願將結果緩存以便兩次相同搜索項的請求不會致使再次向遠程API執行完整請求。一般,若是你在後端執行HTTP請求提供網站內容,你可能但願從新思考如何創建你的應用。

考慮到這一點,在下一組示例中,咱們將看看如何在前端使用像JavaScript這樣的工具處理異步應用,讓客戶端承擔更多工做,以提升你應用的擴展性。

5.2 使用Tornado進行長輪詢

Tornado異步架構的另外一個優點是它可以輕鬆處理HTTP長輪詢。這是一個處理實時更新的方法,它既能夠應用到簡單的數字標記通知,也能夠實現複雜的多用戶聊天室。

部署提供實時更新的Web應用對於Web程序員而言是一項長期的挑戰。更新用戶狀態、發送新消息提醒、或者任何一個須要在初始文檔完成加載後由服務器向瀏覽器發送消息方法的全局活動。一個早期的方法是瀏覽器以一個固定的時間間隔向服務器輪詢新請求。這項技術帶來了新的挑戰:輪詢頻率必須足夠快以便通知是最新的,但又不能太頻繁,當成百上千的客戶端持續不斷的打開新的鏈接會使HTTP請求面臨嚴重的擴展性挑戰。頻繁的輪詢使得Web服務器遭受」凌遲」之苦。

所謂的」服務器推送」技術容許Web應用實時發佈更新,同時保持合理的資源使用以及確保可預知的擴展。對於一個可行的服務器推送技術而言,它必須在現有的瀏覽器上表現良好。最流行的技術是讓瀏覽器發起鏈接來模擬服務器推送更新。這種方式的HTTP鏈接被稱爲長輪詢或Comet請求。

長輪詢意味着瀏覽器只需啓動一個HTTP請求,其鏈接的服務器會有意保持開啓。瀏覽器只須要等待更新可用時服務器」推送」響應。當服務器發送響應並關閉鏈接後,(或者瀏覽器端客戶請求超時),客戶端只需打開一個新的鏈接並等待下一個更新。

本節將包括一個簡單的HTTP長輪詢實時應用以及證實Tornado架構如何使這些應用更簡單。

5.2.1 長輪詢的好處

HTTP長輪詢的主要吸引力在於其極大地減小了Web服務器的負載。相對於客戶端製造大量的短而頻繁的請求(以及每次處理HTTP頭部產生的開銷),服務器端只有當其接收一個初始請求和再次發送響應時處理鏈接。大部分時間沒有新的數據,鏈接也不會消耗任何處理器資源。

瀏覽器兼容性是另外一個巨大的好處。任何支持AJAX請求的瀏覽器均可以執行推送請求。不須要任何瀏覽器插件或其餘附加組件。對比其餘服務器端推送技術,HTTP長輪詢最終成爲了被普遍使用的少數幾個可行方案之一。

咱們已經接觸過長輪詢的一些使用。實際上,前面提到的狀態更新、消息通知以及聊天消息都是目前流行的網站功能。像Google Docs這樣的站點使用長輪詢同步協做,兩我的能夠同時編輯文檔並看到對方的改變。Twitter使用長輪詢指示瀏覽器在新狀態更新可用時展現通知。Facebook使用這項技術在其聊天功能中。長輪詢如此流行的一個緣由是它改善了應用的用戶體驗:訪客再也不須要不斷地刷新頁面來獲取最新的內容。

5.2.2 示例:實時庫存報告

這個例子演示了一個根據多個購物者瀏覽器更新的零售商庫存實時計數服務。這個應用提供一個帶有」Add to Cart」按鈕的HTML書籍細節頁面,以及書籍剩餘庫存的計數。一個購物者將書籍添加到購物車以後,其餘訪問這個站點的訪客能夠馬上看到庫存的減小。

爲了提供庫存更新,咱們須要編寫一個在初始化處理方法調用後不會當即關閉HTTP鏈接的RequestHandler子類。咱們使用Tornado內建的asynchronous裝飾器完成這項工做,如代碼清單5-4所示。

 

 

讓咱們在看模板和腳本文件以前先詳細看下shopping_cart.py。咱們定義了一個ShoppingCart類來維護咱們的庫存中商品的數量,以及把商品加入購物車的購物者列表。而後,咱們定義了DetailHandler用於渲染HTML;CartHandler用於提供操做購物車的接口;StatusHandler用於查詢全局庫存變化的通知。

DetailHandler爲每一個頁面請求產生一個惟一標識符,在每次請求時提供庫存數量,並向瀏覽器渲染index.html模板。CartHandler爲瀏覽器提供了一個API來請求從訪客的購物車中添加或刪除物品。瀏覽器中運行的JavaScript提交POST請求來操做訪客的購物車。咱們將在下面的StatusHandler和ShoppingCart類的講解中看到這些方法是如何做用域庫存數量查詢的。

 

 

關於StatusHandler首先須要注意的是get方法上面的@tornado.web.asynchronous裝飾器。這使得Tornado在get方法返回時不會關閉鏈接。在這個方法中,咱們只是註冊了一個帶有購物車控制器的回調函數。咱們使用self.async_callback包住回調函數以確保回調函數中引起的異常不會使RequestHandler關閉鏈接。

tip在Tornado 1.1以前的版本中,回調函數必須被包在self.async_callback()方法中來捕獲被包住的函數可能會產生的異常。不過,在Tornado 1.1或更新版本中,這再也不是顯式必須的了。

 

 

每當訪客操做購物車,ShoppingCart控制器爲每一個已註冊的回調函數調用on_message方法。這個方法將當前庫存數量寫入客戶端並關閉鏈接。(若是服務器不關閉鏈接的話,瀏覽器可能不會知道請求已經被完成,也不會通知腳本有過更新。)既然長輪詢鏈接已經關閉,購物車控制器必須刪除已註冊的回調函數列表中的回調函數。在這個例子中,咱們只須要將回調函數列表替換爲一個新的空列表。在請求處理中被調用並完成後刪除已註冊的回調函數十分重要,由於隨後在調用回調函數時將在以前已關閉的鏈接上調用finish(),這會產生一個錯誤。

最後,ShoppingCart控制器管理庫存分批和狀態回調。StatusHandler經過register方法註冊回調函數,即添加這個方法到內部的callbacks數組。

 

 

此外,ShoppingCart控制器還實現了CartHandler中的addItemToCart和removeItemFromCart。當CartHandler調用這些方法,請求頁面的惟一標識符(傳給這些方法的session變量)被用於在調用notifyCallbacks以前標記庫存。[2]

 

 

已註冊的回調函數被以當前可用庫存數量調用,而且回調函數列表被清空以確保回調函數不會在一個已經關閉的鏈接上調用。

代碼清單5-5是展現書籍列表變化的模板。

 

 

當DetailHandler渲染index.html模板時,咱們只是渲染了圖書的詳細信息幷包含了必需的的JavaScript代碼。此外,咱們經過session變量動態地包含了一個惟一ID,並以count變量保存當前庫存值。

最後,咱們將討論客戶端的JavaScript代碼。因爲這是一本關於Tornado的書籍,所以咱們直到如今一直使用的是Python,而這個例子中的客戶端代碼是相當重要的,咱們至少要可以理解它的要點。在代碼清單5-6中,咱們使用了jQuery庫來協助定義瀏覽器的頁面行爲。

 

 

當文檔完成加載時,咱們爲」Add to Cart」按鈕添加了點擊事件處理函數,並隱藏了」Remove form Cart」按鈕。這些事件處理函數關聯服務器的API調用,並交換添加到購物車接口和從購物車移除接口。

 

 

requestInventory函數在頁面完成加載後通過一個短暫的延遲再進行調用。在函數主體中,咱們經過到/cart/status的HTTP GET請求初始化一個長輪詢。延遲容許在瀏覽器完成渲染頁面時使加載進度指示器完成,並防止Esc鍵或中止按鈕中斷長輪詢請求。當請求成功返回時,count的內容更新爲當前的庫存量。圖5-5所示爲展現所有庫存的兩個瀏覽器窗口。

Figure5-5
圖5-5 長輪詢示例:所有庫存

如今,當你運行服務器,你將能夠加載根URL並看到書籍的當前庫存數量。打開多個細節頁的瀏覽器窗口,並在其中一個窗口點擊」Add to Cart」按鈕。其他窗口的剩餘庫存數量會馬上更新,若是5-6所示。

Figure5-6
圖5-6 長輪詢示例:一個物品在購物車中

這是一個很是簡單的購物車實現,能夠確定的是–沒有邏輯確保咱們不會跌破總庫存量,更不用說數據沒法在Tornado應用的不一樣調用間或同一服務器並行的應用實例間保留。咱們將這些改善做爲練習留給讀者。

5.2.3 長輪詢的缺陷

正如咱們所看到的,HTTP長輪詢在站點或特定用戶狀態的高度交互反饋通訊中很是有用。但咱們也應該知道它的一些缺陷。

當使用長輪詢開發應用時,記住對於瀏覽器請求超時間隔沒法控制是很是重要的。由瀏覽器決定在任何中斷狀況下從新開啓HTTP鏈接。另外一個潛在的問題是許多瀏覽器限制了對於打開的特定主機的併發請求數量。當有一個鏈接保持空閒時,剩下的用來下載網站內容的請求數量就會有限制。

此外,你還應該明白請求是怎樣影響服務器性能的。再次考慮購物車應用。因爲在庫存變化時全部的推送請求同時應答和關閉,使得在瀏覽器從新創建鏈接時服務器受到了新請求的猛烈衝擊。對於像用戶間聊天或消息通知這樣的應用而言,只有少數用戶的鏈接會同時關閉,這就再也不是一個問題了。

5.3 Tornado與WebSockets

WebSockets是HTML5規範中新提出的客戶-服務器通信協議。這個協議目前還是草案,只有最新的一些瀏覽器能夠支持它。可是,它的好處是顯而易見的,隨着支持它的瀏覽器愈來愈多,咱們將看到它愈來愈流行。(和以往的Web開發同樣,必須謹慎地堅持依賴可用的新功能並能在必要時回滾到舊技術的務實策略。)

WebSocket協議提供了在客戶端和服務器間持久鏈接的雙向通訊。協議自己使用新的ws://URL格式,但它是在標準HTTP上實現的。經過使用HTTP和HTTPS端口,它避免了從Web代理後的網絡鏈接站點時引入的各類問題。HTML5規範不僅描述了協議自己,還描述了使用WebSockets編寫客戶端代碼所須要的瀏覽器API。

因爲WebSocket已經在一些最新的瀏覽器中被支持,而且Tornado爲之提供了一些有用的模塊,所以來看看如何使用WebSockets實現應用是很是值得的。

5.3.1 Tornado的WebSocket模塊

Tornado在websocket模塊中提供了一個WebSocketHandler類。這個類提供了和已鏈接的客戶端通訊的WebSocket事件和方法的鉤子。當一個新的WebSocket鏈接打開時,open方法被調用,而on_message和on_close方法分別在鏈接接收到新的消息和客戶端關閉時被調用。

此外,WebSocketHandler類還提供了write_message方法用於向客戶端發送消息,close方法用於關閉鏈接。

 

 

正如你在咱們的EchoHandler實現中所看到的,open方法只是使用WebSocketHandler基類提供的write_message方法向客戶端發送字符串」connected!」。每次處理程序從客戶端接收到一個新的消息時調用on_message方法,咱們的實現中將客戶端提供的消息原樣返回給客戶端。這就是所有!讓咱們經過一個完整的例子看看實現這個協議是如何簡單的吧。

5.3.2 示例:使用WebSockets的實時庫存

在本節中,咱們能夠看到把以前使用HTTP長輪詢的例子更新爲使用WebSockets是如何簡單。可是,請記住,WebSockets仍是一個新標準,只有最新的瀏覽器版本能夠支持它。Tornado支持的特定版本的WebSocket協議版本只在Firefox 6.0或以上、Safari 5.0.1或以上、Chrome 6或以上、IE 10預覽版或以上版本的瀏覽器中可用。

不去管免責聲明,讓咱們先看看源碼吧。除了服務器應用須要在ShoppingCart和StatusHandler類中作一些修改外,大部分代碼保持和以前同樣。代碼清單5-7看起來會很熟悉。

 

 

除了額外的導入語句外,咱們只須要改變ShoppingCart和StatusHandler類。首先須要注意的是,爲了得到WebSocketHandler的功能,須要使用tornado.websocket模塊。

在ShoppingCart類中,咱們只須要在通知回調函數的方式上作一個輕微的改變。由於WebSOckets在一個消息發送後保持打開狀態,咱們不須要在它們被通知後移除內部的回調函數列表。咱們只須要迭代列表並調用帶有當前庫存量的回調函數:

 

 

另外一個改變是添加了unregisted方法。StatusHandler會在WebSocket鏈接關閉時調用該方法移除一個回調函數。

 

 

大部分改變是在繼承自tornado.websocket.WebSocketHandler的StatusHandler類中的。WebSocket處理函數實現了open和on_message方法,分別在鏈接打開和接收到消息時被調用,而不是爲每一個HTTP方法實現處理函數。此外,on_close方法在鏈接被遠程主機關閉時被調用。

 

 

在實現中,咱們在一個新鏈接打開時使用ShoppingCart類註冊了callback方法,並在鏈接關閉時註銷了這個回調函數。由於咱們依然使用了CartHandler類的HTTP API調用,所以不須要監聽WebSocket鏈接中的新消息,因此on_message實現是空的。(咱們覆寫了on_message的默認實現以防止在咱們接收消息時Tornado拋出NotImplementedError異常。)最後,callback方法在庫存改變時向WebSocket鏈接寫消息內容。

這個版本的JavaScript代碼和以前的很是類似。咱們只須要改變其中的requestInventory函數。咱們使用HTML5 WebSocket API取代長輪詢資源的AJAX請求。參見代碼清單5-8.

 

 

在建立了一個到ws://localhost:8000/cart/status的心得WebSocket鏈接後,咱們爲每一個但願響應的事件添加了處理函數。在這個例子中咱們惟一關心的事件是onmessage,和以前版本的requestInventory函數同樣更新count的內容。(輕微的不一樣是咱們必須手工解析服務器送來的JSON對象。)

就像前面的例子同樣,在購物者添加書籍到購物車時庫存量會實時更新。不一樣之處在於一個持久的WebSocket鏈接取代了每次長輪詢更新中從新打開的HTTP請求。

5.3.3 WebSockets的將來

WebSocket協議目前還是草案,在它完成時可能還會修改。然而,由於這個規範已經被提交到IETF進行最終審查,相對而言不太可能會再面臨重大的改變。正如本節開頭所提到的那樣,WebSocket的主要缺陷是目前只支持最新的一些瀏覽器。

儘管有上述警告,WebSockets仍然是在瀏覽器和服務器之間實現雙向通訊的一個有前途的新方法。當協議獲得了普遍的支持後,咱們將開始看到更加著名的應用的實現。

—————————————————
[1] 書中網頁已不存在,替換爲當前網址。
[2] 下面的這組代碼書中使用的不是前面的代碼,這裏爲了保持一致修改成和前面的代碼同樣。

========================================================
各章連接:
《Introduction to Tornado》中文翻譯計劃——第一章:引言
《Introduction to Tornado》中文翻譯計劃——第二章:表單和模板
《Introduction to Tornado》中文翻譯計劃——第三章:模板擴展
《Introduction to Tornado》中文翻譯計劃——第四章:數據庫
《Introduction to Tornado》中文翻譯計劃——第五章:異步Web服務
《Introduction to Tornado》中文翻譯計劃——第六章:編寫安全應用
《Introduction to Tornado》中文翻譯計劃——第七章:外部服務認證
《Introduction to Tornado》中文翻譯計劃——第八章:部署Tornado

本文內容聽從CC3.0版權協議,轉載請註明:轉自Pythoner

本文連接地址:《Introduction to Tornado》中文翻譯計劃——第五章:異步Web服務

相關文章
相關標籤/搜索