繁体   English   中英

Web服务中的扭曲延迟与阻塞

[英]Twisted deferred vs blocking in web services

我正在努力在使用Deferred对象的Web服务代码中产生与未使用的对象相同的行为。 我的目标是编写一个装饰器,该装饰器将把任何方法(从Twisted解耦)的处理委托给Twisted线程池,以便不阻塞反应堆,而无需更改该方法的任何语义。

当下面的类echo实例作为Web服务公开时,此代码:

from twisted.web import server, resource
from twisted.internet import defer, threads
from cgi import escape
from itertools import count

class echo(resource.Resource):
  isLeaf = True
  def errback(self, failure): return failure
  def callback1(self, request, value):
    #raise ValueError  # E1
    lines = ['<html><body>\n',
             '<p>Page view #%s in this session</p>\n' % (value,),
             '</body></html>\n']
    return ''.join(lines)
  def callback2(self, request, encoding):
    def execute(message):
      #raise ValueError  # E2
      request.write(message.encode(encoding))
      #raise ValueError  # E3
      request.finish()
      #raise ValueError  # E4
      return server.NOT_DONE_YET
    return execute
  def render_GET(self, request):
    content_type, encoding = 'text/html', 'UTF-8'
    request.setHeader('Content-Type', '%s; charset=%s' %
        tuple(map(str, (content_type, encoding))))
    s = request.getSession()
    if not hasattr(s, 'counter'):
      s.counter = count(1)
    d = threads.deferToThread(self.callback1, request, s.counter.next())
    d.addCallback(self.callback2(request, encoding))
    d.addErrback(self.errback)
    #raise ValueError  # E5
    return server.NOT_DONE_YET

当所有的抬高语句都被注释掉时,它将在浏览器中显示一个HTML文档,并且当包含标有“ E5”的抬高语句时,将显示格式正确的堆栈跟踪(Twisted对我来说是这样做的)。 那就是我想要的。 同样,如果我根本不使用Deferred对象,而是将所有来自callback1和callback2的行为放在render_GET()中,则render_GET中任何地方引发的异常都会产生所需的堆栈跟踪。

我正在尝试编写将立即对浏览器作出响应的代码,而不导致Twisted内部资源泄漏,并且在其中包含任何抬高语句“ E1”至“ E3”的情况下,也显示浏览器堆栈跟踪延迟的代码-尽管我当然知道堆栈跟踪本身会有所不同。 (我不太关心“ E4”情况。)在阅读Twisted文档和本网站上的其他问题之后,我不确定如何实现此目的。 我本以为添加错误后备功能会很方便,但显然不会。 关于Deferred对象和twisted.web堆栈,肯定有些我不了解的东西。

我使用PythonLoggingObserver将Twisted日志记录桥接到标准日志记录模块时,可能会影响此处记录的日志记录的影响。

当包含“ E1”时,浏览器将等待直到反应堆关闭为止,此时将记录带有堆栈跟踪的ValueError异常,并且浏览器将收到一个空文档。

当包含“ E2”时,将立即记录带有堆栈跟踪的ValueError异常,但浏览器将一直等待,直到反应堆关闭为止,此时反应堆将收到一个空文档。

当包含“ E3”时,将立即记录带有堆栈跟踪的ValueError异常,浏览器将等待直到反应堆关闭,然后接收到预期的文档。

当包含引发语句“ E4”时,预期文档立即返回浏览器,并立即记录带有堆栈跟踪的ValueError异常。 (在这种情况下是否有资源泄漏的可能性?)

好吧,我看了几次你的问题后,我想我明白你在问什么。 我还对您的代码进行了改进,使其比原始答案要好一些。 这个新的答案应该展示出延期制的所有功能。

from twisted.web import server, resource
from twisted.internet import defer, threads
from itertools import count

class echo(resource.Resource):
  isLeaf = True
  def errback(self, failure, request):
    failure.printTraceback() # This will print the trace back in a way that looks like a python exception.
    # log.err(failure) # This will use the twisted logger. This is the best method, but
    # you need to import twisted log.

    request.processingFailed(failure) # This will send a trace to the browser and close the request.
    return None #  We have dealt with the failure. Clean it out now.

  def final(self, message, request, encoding): 
    # Message will contain the message returned by callback1
    request.write(message.encode(encoding)) # This will write the message and return it to the browser.

    request.finish() # Done

  def callback1(self, value):
    #raise ValueError  # E1
    lines = ['<html><body>\n',
             '<p>Page view #%s in this session</p>\n' % (value,),
             '</body></html>\n']
    return ''.join(lines)

    #raise ValueError  # E4

  def render_GET(self, request):
    content_type, encoding = 'text/html', 'UTF-8'
    request.setHeader('Content-Type', '%s; charset=%s' %
        tuple(map(str, (content_type, encoding))))
    s = request.getSession()
    if not hasattr(s, 'counter'):
      s.counter = count(1)
    d = threads.deferToThread(self.callback1, s.counter.next())
    d.addCallback(self.final, request, encoding)
    d.addErrback(self.errback, request) # We put this here in case the encoding raised an exception.
    #raise ValueError  # E5
    return server.NOT_DONE_YET

另外,我建议您阅读krondo教程。 它会教您有关延迟所需的所有知识。

编辑:

修改了上面的代码以修复一些愚蠢的错误。 也有所改善。 如果异常发生在任何地方( self.errback除外,但我们需要某种程度的信任),那么它将被传递到self.errback ,它将以扭曲方式记录或打印错误,然后将跟踪发送到浏览器关闭请求。 这应该停止任何资源泄漏。

我通过深入了解Twisted源找出了答案。 必要的见解是反应堆和Deferred回调/错误回溯链逻辑与请求对象分离,这就是数据如何返回到浏览器的方式。 犯错是必要的,但不能像我发布的原始代码中那样仅在链下传播Failure对象。 错误回复必须将错误报告给浏览器。

以下代码符合我的要求(从不让浏览器等待,始终提供堆栈跟踪,不需要重新启动反应堆即可使事情再次发生),并允许我修饰阻塞方法,从而将它们委派给线程以保持反应堆响应其他事件(在这里,此类方法将基本上代替callback1)。 但是,我确实发现,在下面的代码中,取消注释“ E4”引发语句会在后续的浏览器请求上产生非常奇怪的行为(先前请求的部分数据返回到浏览器;死锁)。

希望其他人会发现这是一个有用的Deferred示例。

from twisted.web import server, resource
from twisted.internet import defer, threads
from itertools import count

class echo(resource.Resource):
  isLeaf = True
  def errback(self, request):
    def execute(failure):
      request.processingFailed(failure)
      return failure
    return execute
  def callback1(self, value):
    #raise ValueError  # E1
    lines = ['<html><body>\n',
             '<p>Page view #%s in this session</p>\n' % (value,),
             '</body></html>\n']
    return ''.join(lines)
  def callback2(self, request, encoding):
    def execute(message):
      #raise ValueError  # E2
      request.write(message.encode(encoding))
      #raise ValueError  # E3
      request.finish()
      #raise ValueError  # E4
      return server.NOT_DONE_YET
    return execute
  def render_GET(self, request):
    content_type, encoding = 'text/html', 'UTF-8'
    request.setHeader('Content-Type', '%s; charset=%s' %
        tuple(map(str, (content_type, encoding))))
    s = request.getSession()
    if not hasattr(s, 'counter'):
      s.counter = count(1)
    d = threads.deferToThread(self.callback1, s.counter.next())
    eback = self.errback(request)
    d.addErrback(eback)
    d.addCallback(self.callback2(request, encoding))
    d.addErrback(eback)
    #raise ValueError  # E5
    return server.NOT_DONE_YET

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM