2016-08-02 57 views
6

我有我的蟒蛇絞克萊恩Web服務兩個功能:如何在Twisted Klein中異步執行代碼?

@inlineCallbacks 
def logging(data): 
    ofile = open("file", "w") 
    ofile.write(data) 
    yield os.system("command to upload the written file") 

@APP.route('/dostuff') 
@inlineCallbacks 
def dostuff(): 
    yield logging(data) 
    print "check!" 
    returnValue("42") 

os.system("command to upload the written file")運行時,它會顯示消息說「開始上傳」,然後「上傳完成」。我想使日誌記錄功能異步,以便logging處理程序中的處理髮生在dostuff處理程序打印出「check!」之後。 (我實際上想要在returnValue(「42」)之後發生處理,但是這兩種都使得日誌功能異步,我認爲?)

我認爲yield語句會使其非阻塞,但似乎並非如此, 支票!」在「開始上傳」和「上傳完成」後總是打印。我會很感激,如果任何人都可以給我一些反饋意見,因爲我是新來的異步編碼,並在這一段時間被阻止...

回答

3

要使代碼異步,您需要使用Twisted Deferreds作爲described here。延遲爲您提供異步代碼執行的API,它們允許您將回調附加到您的函數,並在由reactor對象管理的Twisted事件循環中執行代碼。

我看到兩種可能的方式來使用延遲你的情況。

1)在後臺執行的任務與reactor.callLater()

這是確定的,如果dostuff處理程序不關心結果。您可以使用reactor.callLater()。這樣您的異步函數將在您從doStuff返回值後執行。

因此,像這樣:

from klein import run, route, Klein 
from twisted.internet import defer, task, reactor 
import os 

app = Klein() 


def logging(data): 
    ofile = open("file", "w") 
    ofile.write(data) 
    result = os.system("ls") 
    print(result) 


@route('/') 
def dostuff(request): 
    reactor.callLater(0, logging, "some data") 
    print("check!") 
    return b'Hello, world!' 

run("localhost", 8080) 

與此代碼的事件順序如下,第一個「檢查」被打印出來,然後在「Hello World」的響應返回,並最終異步調用suceeds並打印運行結果爲os.system()

2016-08-11 08:52:33+0200 [-] check! 
2016-08-11 08:52:33+0200 [-] "127.0.0.1" - - [11/Aug/2016:06:52:32 +0000] "GET/HTTP/1.1" 200 13 "-" "curl/7.35.0" 
a.py file 

2)在後臺執行任務,並與task.deferLater()

得到的結果,如果你關心你的「記錄」功能的結果,你還可以將回調到該對象和使用twisted.internet.task API。如果你想要走這條路,你需要重構自己的處理程序是這樣

@route('/') 
def dostuff(request): 
    def the_end(result): 
     print("executed at the end with result: {}".format(result)) 
    dfd = task.deferLater(reactor, 0, logging, "some data") 
    dfd.addCallback(the_end) 
    print("check!") 
    return b'Hello, world!' 

工作這一事件的方式訂貨會和上面一樣,但the_end功能將在年底後您的logging函數完成執行。

2016-08-11 08:59:24+0200 [-] check! 
2016-08-11 08:59:24+0200 [-] "127.0.0.1" - - [11/Aug/2016:06:59:23 +0000] "GET/HTTP/1.1" 200 13 "-" "curl/7.35.0" 
a.py file 
2016-08-11 08:59:24+0200 [-] executed at the end with result: some result 
+0

因爲我對扭曲的Klein的知識主要來自http://tavendo.com/blog/post/going-asynchronous-from-flask-to-twisted-klein/您的回答非常有幫助。我還沒有嘗試,但我會認爲它是正確的。非常感謝你。真的很感激它。 – JLTChiu

+0

作爲一個更新,代碼工作,但我的pyCharm和pylint顯示錯誤'E:512,4:模塊'twisted.internet.reactor'沒有'callLater'成員(無成員)'但問題是這個代碼似乎完全好(它按預期工作)。有沒有辦法來解決這個問題? – JLTChiu

+1

看起來更像是pycharm代碼檢查中的一些錯誤,可能是平臺特定的東西?在做這種檢查時,你必須檢查pycharm的外觀,以及爲什麼這個代碼會失敗。我自己使用pycharm,我不太相信它的風格警告,其中許多是虛驚一場。 flake8是檢查python代碼風格的更好工具IMO –

1

'yield'語句不會使事情異步發生。它只是推遲執行包含它的函數,並返回一個生成器對象,稍後可用於迭代序列。

所以dostuff()將返回一個生成器對象。除非發生器對象稍後迭代,否則什麼都不會發生。但是你的代碼中沒有任何東西可以實現這一點。我期望你的dostuff例程會產生一個語法錯誤,因爲它包含一個yield和一個非空return。日誌記錄例程不會執行任何操作,因爲它包含一個yield,它返回的生成器從不使用。

最後,日誌記錄例程將在每次調用時截斷其輸出文件,因爲它會在每次調用時打開模式爲'w'的日誌文件。

對於異步執行,您需要某種形式的多處理。但我認爲在這種情況下這不是必要的。您的日誌記錄功能重量輕,應該快速運行,不會影響撲克的工作。

我建議想是這樣的:

@inlineCallbacks 
def logging(data): 
    try: 
     logging._ofile.write(data + '\n') 
    except AttributeError: 
     logging._ofile = open("file", 'w') 
     logging._ofile.write(data + '\n') 

@APP.route('/dostuff') 
@inlineCallbacks 
def dostuff(): 
    logging("before!") 
    os.system("command to upload the written file") 
    logging("after!") 
    return("42") 

這裏我們打開日誌文件只有一次,當_ofile沒有被定義爲記錄的屬性第一次記錄被調用。在隨後的調用中,logging._ofile已經打開,try塊中的寫入語句會成功。

例程dostuff()調用日誌記錄來指示我們即將完成工作,實際完成工作,然後調用日誌記錄以指示工作已完成,最後返回所需的值。

+0

dostuff是不是你的代碼示例異步,它會阻止對'os.system'調用,因此「開始上傳」和「上傳完成」從dostuff –

+0

真在返回前將被打印出來。感謝您選擇Twisted知識。 –