【问题标题】:How to execute code asynchronously in Twisted Klein?如何在 Twisted Klein 中异步执行代码?
【发布时间】:2016-12-08 08:32:14
【问题描述】:

我的 python Twisted Klein web 服务中有两个函数:

@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 处理程序打印出“检查!”之后。 (我实际上希望在 returnValue("42") 之后进行处理,但我认为这两个都使日志记录函数异步?)

我认为 yield 语句会使其非阻塞,但似乎不是这样,“检查!”总是在“开始上传”和“上传完成”后打印出来。如果有人能给我一些反馈,我将不胜感激,因为我是异步编码的新手并且被阻止了一段时间......

【问题讨论】:

    标签: python web-services asynchronous twisted klein-mvc


    【解决方案1】:

    要使您的代码异步,您需要将Twisted Deferreds 用作described here。 Deferreds 为您提供了一个用于异步代码执行的 API,它们允许您将回调附加到您的函数,并且它们在由 reactor 对象管理的 Twisted 事件循环中执行代码。

    在您的案例中,我看到了两种使用 Deferreds 的潜在方法。

    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”响应,最后异步调用成功并打印运行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
    

    【讨论】:

    • 由于我对twisted Klein的了解主要来自tavendo.com/blog/post/…您的回复对我帮助很大。我还没有尝试过,但我会假设它是正确的。非常感谢。真的很感激。
    • 作为更新,该代码有效,但我的 pyCharm 和 pylint 在 E:512, 4: Module 'twisted.internet.reactor' has no 'callLater' member (no-member) 上显示错误,但问题是该代码似乎完全正常(它按预期工作)。有没有办法解决这个问题?
    • 看起来更像是 pycharm 代码检查中的一些错误,可能是特定于平台的东西?在进行这样的检查时,您必须检查 pycharm 查找的内容以及为什么它会因这段特定的代码而失败。我自己使用 pycharm,我不太相信它的风格警告,其中很多都是误报。 flake8 是检查 python 代码样式 IMO 的更好工具
    • @JLTChiu Reactor 对象是生成的成员类,因此您必须忽略 pylintrc 文件中的 reactor.callLater() 之类的调用。皮林特超级烦人……
    【解决方案2】:

    'yield' 语句不会让事情异步发生。它只是延迟执行包含它的函数并返回一个生成器对象,该对象稍后可用于迭代序列。

    所以 dostuff() 将返回一个生成器对象。直到稍后某个生成器对象被迭代,什么都不会发生。但是您的代码中没有任何内容可以实现这一点。我希望你的 dostuff 例程会产生一个语法错误,因为它同时包含一个 yield 和一个非空返回。日志记录例程不会做任何事情,因为它包含一个 yield 并且它返回的生成器从未使用过。

    最后,日志记录例程将在每次调用时截断其输出文件,因为它会在每次调用时以“w”模式打开日志文件。

    对于异步执行,您需要某种形式的多处理。但我认为在这种情况下不需要这样做。您的日志记录功能相当轻巧,应该可以快速运行并且不会干扰 dostuff 的工作。

    我建议尝试这样的事情:

    @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")
    

    这里我们只打开一次 logging 文件,第一次 logging 是在 _ofile 没有定义为 logging 的属性时调用的。在后续调用中,logging._ofile 将已经打开,并且 try 块中的 write 语句将成功。

    例程 dostuff() 调用 logging 表示我们即将完成工作,实际完成工作,然后调用 logging 表示工作已完成,最后返回所需的值。

    【讨论】:

    • dostuff 在您的代码示例中不是异步的,它会阻止os.system 调用,因此在从 dostuff 返回之前会打印“开始上传”和“上传完成”
    • 是的。感谢您提供 Twisted 知识。
    猜你喜欢
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 2019-02-09
    • 1970-01-01
    相关资源
    最近更新 更多