2016-08-02 55 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知识。 –