【发布时间】:2012-01-22 09:34:19
【问题描述】:
我正在编写一个 wsgi 应用程序,它需要使用 Twisted PerspectiveBroker 来调用一些远程方法。问题是 wsgi 需要返回一个呈现的网页,但对 Twisted 服务的调用是异步的。所以基本上我的网络应用需要调用远程方法,然后做一些其他的事情,然后它必须等待远程调用完成,然后渲染页面并将其返回给客户端。
最好的方法是什么?
我目前正计划使用 Flask 来编写应用程序。
【问题讨论】:
我正在编写一个 wsgi 应用程序,它需要使用 Twisted PerspectiveBroker 来调用一些远程方法。问题是 wsgi 需要返回一个呈现的网页,但对 Twisted 服务的调用是异步的。所以基本上我的网络应用需要调用远程方法,然后做一些其他的事情,然后它必须等待远程调用完成,然后渲染页面并将其返回给客户端。
最好的方法是什么?
我目前正计划使用 Flask 来编写应用程序。
【问题讨论】:
WSGI 应用程序在它自己的线程(或进程)中运行。当在 Twisted 的 WSGI 容器中运行时,它与运行反应器的线程不同。大多数 Twisted 的 API 不是线程安全的:它们只能在反应器线程中调用。
因此,从 WSGI 应用程序调用 Twisted API 的基本方法是使用 reactor.callFromThread,它是线程安全的,并且会导致在反应器线程中调用函数:
...
reactor.callFromThread(pbRemote.callRemote, "someMethod", some, args)
但是,这会丢弃您可能想要的结果。不过,在 reactor.callFromThread 之上构建一个保留结果的 API 很简单,而且在 Twisted 中也有一个实现:
from twisted.internet.threads import blockingCallFromThread
...
result = blockingCallFromThread(reactor, pbRemote.callRemote, "someMethod", some, args)
此调用将阻塞,直到 callRemote 返回的 Deferred 触发,然后它将返回该 Deferred 的结果。
如果你想打电话,做一些其他的工作,然后等待电话结束,你必须有点创意。您需要拨打电话并获取它返回的实际Deferred,但不要阻止它:
resultHolder = blockingCallFromThread(
reactor, lambda: [pbRemote.callRemote("someMethod", some, args)])
然后你可以做你需要做的其他工作。当你准备好等待 PB 调用的结果时:
result = blockingCallFromThread(reactor, lambda: resultHolder[0])
这一切都比在单线程场景中使用 Twisted 更尴尬,因此使用 Twisted Web 的原生 API 确实比构建 WSGI 应用程序更容易。请记住,WSGI 的主要目标之一是允许开发可跨不同服务器(Twisted、Apache 等)移植的应用程序。如果您实际上在 WSGI 应用程序中使用 Twisted API,那么它根本不是可移植的。
【讨论】:
你可以返回 server.NOT_DONE_YET 告诉twisted.web 请求没有完成。然后调用 request.write() 和 request.finish() 来完成请求,例如:
from twisted.web import server, resource
class MyResource(resource.Resource):
def render_GET(self, request):
# the call will return defer, that will notify us when it finish
d = delayCall()
# finish the request
def finisn_req(data):
request.write(data)
request.finish()
d.addCallback(finisn_req)
# tell twisted.web that this request is not finished, yet
return server.NOT_DONE_YET
【讨论】: