Twisted PerspectiveBroker callRemote из веб-приложения wsgi

Я пишу приложение wsgi, которому нужно использовать Twisted PerspectiveBroker для вызова некоторых удаленных методов. Проблема в том, что wsgi должен возвращать обработанную веб-страницу, но вызовы службы Twisted асинхронны. Таким образом, в основном мое веб-приложение должно вызывать удаленные методы, затем выполнять некоторые другие действия, затем ему приходится ждать завершения удаленных вызовов, затем отображать страницу и возвращать ее клиенту.

Как лучше всего это сделать?

В настоящее время я планирую использовать Flask для написания приложения.


person Blubber    schedule 22.01.2012    source источник


Ответы (2)


Приложение WSGI выполняется в собственном потоке (или процессе). При работе в WSGI-контейнере Twisted это другой поток, в котором работает реактор. Большинство API-интерфейсов Twisted не являются потокобезопасными: их можно вызывать только в потоке реактора.

Таким образом, основной способ вызова Twisted API из приложения WSGI — использование reactor.callFromThread, который является потокобезопасным и вызывает вызов функции в потоке реактора:

...
reactor.callFromThread(pbRemote.callRemote, "someMethod", some, args)

Однако это отбрасывает результат, который вы, вероятно, хотите. Тем не менее, создать API поверх reactor.callFromThread, сохраняющий результат, просто, и в Twisted тоже есть такая реализация:

from twisted.internet.threads import blockingCallFromThread

...
result = blockingCallFromThread(reactor, pbRemote.callRemote, "someMethod", some, args)

Этот вызов будет заблокирован до тех пор, пока не сработает Deferred, возвращенный callRemote, а затем он вернет результат этого Deferred.

Если вы хотите сделать звонок, выполнить какую-то другую работу, а затем дождаться завершения разговора, вам придется проявить немного творчества. Вам нужно сделать вызов и получить фактический Deferred, который он возвращает, но не блокировать его:

resultHolder = blockingCallFromThread(
    reactor, lambda: [pbRemote.callRemote("someMethod", some, args)])

И тогда вы можете делать любую другую работу, которую вам нужно сделать. И когда вы будете готовы дождаться результата вызова PB:

result = blockingCallFromThread(reactor, lambda: resultHolder[0])

Все это гораздо более неудобно, чем использование Twisted в однопоточном сценарии, поэтому действительно может быть проще использовать собственные API-интерфейсы Twisted Web, а не создавать приложение WSGI. Помните, что одной из основных целей WSGI является обеспечение возможности разработки приложений, переносимых между разными серверами — Twisted, Apache и т. д. Если вы на самом деле используете Twisted API в своем приложении WSGI, то оно вообще не является переносимым.

person Jean-Paul Calderone    schedule 22.01.2012

Вы можете вернуть 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
person Fang-Pen Lin    schedule 22.01.2012
comment
Должен был упомянуть об этом, я планирую использовать Flask для написания веб-приложения, если только использовать Twisted web не намного проще. - person Blubber; 22.01.2012
comment
Тогда как насчет того, чтобы написать брокера, принять XML-RPC с Twisted, позвонить на ваш сервер. Flask -> Брокер XML-RPC -> Ваш сервер? - person Fang-Pen Lin; 22.01.2012