發表文章

目前顯示的是 7月, 2018的文章

[Twisted] Part 19: I Thought I Wanted It But I Changed My Mind

圖片
本文由Dave的 Part 19: I Thought I Wanted It But I Changed My Mind 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction Twisted是一個進行中的專案,Twisted的開發人員會定期的加入新的功能並擴展舊的功能。隨著Twisted 10.1.0的發布,開發人員為Deferred類別加入了一個新的能力-取消(cancellation),我們今天會來探討這個能力。 非同步程式設計將請求從回應中脫離,因而提升了一種新的可能性:在請求結果與取回結果之間,你可能決定你不再需要它了。想一想 Part 14 的詩歌代理伺服器。以下是代理伺服器的運作方式,至少第一次詩歌請求是這樣子: 來了一個詩歌的請求。 代理伺服器聯絡真的伺服器來取得詩歌。 一但詩歌下載完成,將它傳送給原來的用戶端。 這樣看起來都很好,但是如果用戶端在收到詩歌前中止了怎麼辦?也許它們一開始請求 Paradise Lost 的完整文本,後來決定它們真的想要的是 Kojo 的俳句。現在我們的代理伺服器正卡在下載前者,而慢速伺服器還需要一點時間。我們最好關閉這個連接,並且讓慢速伺服器回去睡覺。 回憶一下 圖15 ,那張顯示同步程式控制的概念流程的圖。在那張圖中我們看到函式呼叫是由上而下,而例外是由下而上返回。如果我們想要取消同步函式的呼叫(而這只是假設),流程控制會與函式呼叫的方向相同,像圖38從高層程式碼到底層程式碼: 圖38.有假設性取消的同步程式流程 當然,在同步程式中這是不可能的,因為直到底層程式碼操作完成前,高層程式碼根本不會恢復執行,在這個時間根本沒有東西可以取消。 但是在非同步程式中,高層程式碼在底層程式碼完成前得到控制權,這至少提升了在底層請求完成之前取消它的可能性。 在Twisted程式中,底層請求是由Deferred物件來具體化的,你可以想像它是未完成的非同步操作的「handle」。在deferred實例中訊息的一般流程是向下的,從底層程式碼到高層程式碼,這與同步程式中的回傳訊息流程一致。從Twisted 10.1.0開始,高層程式碼可以向反方向傳送訊息-這樣可以告訴底層程式碼它不在需要底層程式碼的結果了。看看圖39: 圖39.在def...

[Twisted] Part 18: Deferreds En Masse

圖片
本文由Dave的 Part 18: Deferreds En Masse 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction 在上個章節,我們學習了使用生成器建構連續的非同步callbacks的新方法。因此,包括了deferreds,我們現在有兩種技術將非同步操作鏈接在一起。 然而,有時候我們希望以「並行(parallel)」的方式執行一組(group)非同步操作。由於Twisted是單執行緒的,它們不會真正的同時執行,但重點在我們希望使用非同步I/O,盡快的處理一組任務(tasks)。例如,我們的詩歌用戶端可以同時從多個伺服器下載詩歌,而不是一個伺服器接著另外一個。畢竟,這就是使用Twisted取得詩歌的重點。 因此,我們所有的詩歌用戶端必須解決這個問題:你怎麼知道你啟動的所有非同步操作完成了?到目前為止,我們已經藉由收集我們的結果到一個串列中(像7.0版本用戶端的 results 串列),並且檢查列表的長度來解決這個問題。我們除了必須小心的收集成功結果之外,還要收集失敗結果,否則一個失敗結果會導致城市認為還有工作要做而永遠執行。 如你所料,Twisted包含了一個抽象讓你可以用來解決這個問題,而我們今天要來看一下。 The DeferredList DeferredList類別讓我們可以將一個deferred物件串列作為一個deferred實例來處理。 這樣我們可以啟動一堆非同步操作,並且只在它們都完成時才得到通知(無論它們成功或失敗)。讓我們看看一些範例。 在 deferred-list/deferred-list-1.py 你可以找到這段程式碼: from twisted.internet import defer def got_results(res): print 'We got:', res print 'Empty List.' d = defer.DeferredList([]) print 'Adding Callback.' d.addCallback(got_results) 如果你執行它,你會得到這樣的輸出: Empty List. Adding Callback. We got:...

[Twisted] Part 17: Just Another Way to Spell “Callback”

圖片
本文由Dave的 Part 17: Just Another Way to Spell “Callback” 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction 在這個章節中我們將回到callback這個主題。我們將介紹另一種在Twisted中撰寫callback的技術,那就是使用 生成器(generator) 。我們會展示這種技術如何運作,並把它與使用「純」Deferreds的技術進行對比。最後,我們將使用這種技術重寫我們的詩歌用戶端。但首先讓我們回顧一下生成器是如何運作的,以便我們可以了解為什麼它是建立callback的候選方法。 A BRIEF REVIEW OF GENERATORS 你可能知道,Python生成器是一種「可重新啟動的函式」,你可以藉由在你的函式主體中使用yield表達式來建立。透過這種做法,該函示成為一個「生成器函式」,它會回傳一個你可以用來以一系列的步驟來執行這個函式的 迭代器(iterator) 。 迭代器的每個循環都會重新啟動這個函式,這個函式會繼續執行直到它到達下一個yield。 (譯註:如果這個生成器函式有多個yeild,第一個迴圈會執行到第一個yield,第二次迴圈會從第一個yield之後開始執行到第二個yield,並以此類推,所以前面才會說這是「一系列的步驟」,因為它是一段一段在執行的) 生成器(和迭代器)通常用來表示以惰性建立(lazily-created)的數值序列(譯註:關於惰性可以參考一下維基的 Lazy evaluation )。看一下 inline-callbacks/gen-1.py 中的範例: def my_generator(): print 'starting up' yield 1 print "workin'" yield 2 print "still workin'" yield 3 print 'done' for n in my_generator(): print n 這裡我們有一個建立了序列1、2、3的生成器。如果你執行程式碼,當迴圈的循環遍歷生成器時,你會...

[Twisted] Part 16: Twisted Daemonologie

圖片
本文由Dave的 Part 16: Twisted Daemonologie 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction 到目前為止我們已經寫過的伺服器只能在終端視窗執行,並透過print statement輸出到螢幕。這是用於開發階段,但作為一個產品來布署服務還遠遠不足。一個作為良好產品的伺服器應該: 作為 daemon 程序執行,與任何終端或使用者session無關。你不會希望服務只是因為管理者登出就被關閉。 將除錯與錯誤訊息傳送到一組輪替(rotate)的log檔,或者 syslog 服務。 放棄過高的權限,例如在執行前切換到低權限的使用者。 在檔案中記錄它的 pid ,以便管理者向daemon 發送信號 。 我們可以使用Twisted提供的twistd script來取得這些功能。但首先我們需要稍微修改我們的程式碼。 The Concepts 要理解twistd script需要學習一些Twisted的新概念,其中最重要的是Service。跟往常一樣,一些新的概念也伴隨著一些新的Interface。 ISERVICE IService 介面定義一個由我們命名的服務,並且可以啟動或停止它。 這個服務要做什麼?任何你喜歡的事情-而不是定義服務的特定函式,這個介面只需要它提供的一些通用的屬性與方法。 有兩個必要的屬性:name和running。Name屬性只是一個字串,像「fastpoetry」,如果你不想為你的服務命名就用None。Running屬性是一個布林值,如果服務已經被成功啟動則為true。 我們只簡單的談談一些IService的方法。我們將跳過一些一看就懂的方法,以及在一簡單Twisted程式中不常使用的進階方法。IService中兩個基本方法為startService與stopService: def startService(): """ Start the service. """ def stopService(): """ Stop the se...

[Twisted] Part 15: Tested Poetry

圖片
本文由Dave的 Part 15: Tested Poetry 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction 我們在探索Twisted時寫了很多程式碼,但目前為止我們忽略了寫一些很重要的東西-測試。而你可能很好奇你如何用同步框架像是Python附帶的 unittest ,來測試非同步的程式碼。簡單來說,你不能。像我們已經發現的,同步與非同步程式碼不能混用,至少不容易。 幸運的是,Twisted包含了自己的測試框架,叫做 trial , 它支援測試非同步程式碼(而且你也可以用它測試同步程式碼)。 我們將假設你已經熟悉了 unittest 與類似測試框架的基本機制,這些框架中你透過定義一個繼承指定父類別(通常叫做TestCase之類的)的類別來建立測試,而類別中每個以「test」開頭的方法就會被認定是一項測試。這些框架負責找出所有的測試方法,配合上選擇性的setUp和tearDown,一起依序執行它們,最後報告結果。 The Example 你可以在 tests/test_poetry.py 找到一些測試的範例。為了確保所有我們的範例都是獨立運作(因此你不必擔心有關PYTHONPATH的設定),我們已經將所有所需的程式碼複製到測試模組中。當然,通常你只需要import你想要測試的模組就好。 這個範例透過由用戶端從測試伺服器取回詩歌,來測試詩歌用戶端與伺服器。為了提供用於測試的詩歌伺服器,我們在我們的測試案例中實現了 setUp 方法: class PoetryTestCase(TestCase): def setUp(self): factory = PoetryServerFactory(TEST_POEM) from twisted.internet import reactor self.port = reactor.listenTCP(0, factory, interface="127.0.0.1") self.portnum = self.port.getHost().port setUp方法以測試詩歌建立詩歌伺服器,並監聽一個隨機的開放埠口。我們會保存這個埠號,這樣...

[Twisted] Part 14: When a Deferred Isn’t

圖片
本文由Dave的 Part 14: When a Deferred Isn’t 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction 在這個部分我們要學習Deferred類別的另一個特點。為了便於討論,我們將會在我們穩定的詩歌相關服務中再增加一個伺服器。假設我們有大量內部用戶端想要從相同的外部伺服器獲得詩歌。但是這個外部伺服器處理速度太慢,並且已經因為網際網路上無止盡的詩歌需求而負載過重。我們不想將所有的用戶端請求都送來這,再造成這個可憐的伺服器的問題。 所以我們將製作一個快取代理伺服器。當用戶端連接到代理伺服器後,代理伺服器會從外部伺服器取回詩歌,或者回傳先前取回的詩歌快取副本。然後我們可以將所有用戶端的連接指向代理伺服器,而我們對外部伺服器造成的負載就微不足道了。我們在圖30中說明了這個步驟: 圖30.快取代理伺服器 考慮一下當用戶端連接到代理伺服器來取得詩歌時會發生什麼事情。如果代理伺服器的快取是空的,則代理伺服器在傳回詩歌給用戶端前必須等待(非同步的)外部伺服器回應。到目前為止,我們已經知道如何使用回傳deferred實例的非同步函式處理這個情況。另一方面,如果快取中已經有一首詩了,代理伺服器便可以無需等待的馬上送回這首詩。所以代理伺服器取得詩歌的內部機制有時是非同步的,有時是同步的。 那麼,如果我們要一個只有在某些時候是非同步的函式,我們該怎麼辦?Twisted提供了幾個選項,這些選項都依靠一個我們還沒在Deferred類別使用到的功能: 你可以在回傳deferred實例給呼叫者之前觸發它。 可以這樣做是因為雖然你不能觸發deferred實例兩次,但是你可以在deferred被觸發後加入callback與errback。當你這樣做的時候,deferred實例只會從上次離開的地方繼續觸發callback/errback鏈。 有一件事情要注意,已經觸發的deferred實例可能會馬上觸發新加入的callback(或errback,取決於deferred實例的狀態),也就是當你加入callback/errback的同時。 看一下圖31,它顯示了已經被觸發的deferred實例: 圖31.已經被觸發的deferred實例 如果我們在這個時候加入另一對callback...

[Twisted] Part 13: Deferred All The Way Down

圖片
本文由Dave的 Part 13: Deferred All The Way Down 翻譯而成,你可以由 Part 1 開始閱讀這個系列的文章,也可以在 這裡 找到整個系列的目錄。 Introduction 回想一下 Part 10 中的5.1版本用戶端。用戶端使用Deferred實例來管理包括呼叫詩歌轉換引擎的 callback鏈 。在 5.1版本用戶端 中,引擎的實現是在用戶端本身由一個同步函式來呼叫。 現在我們想要建立一個使用我們在 Part 12 寫的網路詩歌轉換服務的新的用戶端。但是有個小問題:由於轉換服務是在網路上存取的,所以我們需要使用非同步I/O。這表示我們用於請求轉換的API也必須是非同步的。換句話說,在我們新的用戶端中,try_to_cummingsify callback會回傳Deferred實例。 那麼當一個在deferred實例的鏈中的callback回傳了另一個deferred時會發生什麼事情?讓我們稱第一個deferred為「外部(outer)」deferred,第二個deferred為「內部(inner)」deferred。假設在外部deferred的callback N回傳了內部deferred。而這個callback說「我是非同步的,我要的結果還沒出現」。 由於外部deferred需要以現在callback或errback的結果來呼叫鏈中的下一個callback或errback,所以外部deferred需要等待直到內部deferred被觸發為止。當然,外部deferred也不能阻塞,所以外部deferred會暫停callback鏈的執行,並返回控制權給reactor。 外部deferred如何知道什麼時候恢復執行呢?很簡單,在內部deferred中加入一對callback/errback。如此,當內部deferred被觸發,外部deferred會恢復執行它自己的鏈 (譯註:過去我們講到deferred的觸發有點像「開始執行」的意思,實際上觸發的意思應該是「開始執行,並根據callback/errback鏈傳出結果」的這整個過程,只是過去比較不強調傳出結果這部分,而這邊內部deferred的觸發就必須記得這點才比較好理解文章中想表達的意思) 。如果內部deferred執行成功(即它會呼叫外部deferred添加的c...