Web服务中的扭曲延迟vs阻塞
-
29-10-2019 - |
题
我正在努力在使用延迟对象的web服务代码中产生与不使用延迟对象的代码相同的行为。我的目标是编写一个装饰器,将任何方法(与Twisted解耦)的处理委托给Twisted线程池,以便reactor不会被阻塞,而不会改变该方法的任何语义。
当下面的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
当所有raise语句被注释掉时,将向浏览器显示HTML文档,并在包含标记为"E5"的raise语句时显示一个格式良好的堆栈跟踪(Twisted为我做了)。这就是我想要的。同样,如果我根本不使用延迟对象,并将callback1和callback2的所有行为放在render_GET()内,则render_GET内任何地方引发的异常都会产生所需的堆栈跟踪。
我正在尝试编写代码,这些代码将立即响应浏览器,而不是导致Twisted内的资源泄漏,并导致浏览器堆栈跟踪也显示在延迟代码中包含任何raise语句"E1"到"E3"("E4"的情况下,我不关心那么多。)在阅读本网站上的Twisted文档和其他问题后,我不确定如何实现这一点。我本来认为添加一个错误回传应该有助于这一点,但显然不是。必须有一些关于延迟对象和扭曲的东西。我不明白的web堆栈。
我在这里记录的日志记录的影响可能会受到我使用PythonLoggingObserver将扭曲日志记录桥接到标准日志记录模块的影响。
当包含"E1"时,浏览器等待直到反应堆关闭,此时记录带有堆栈跟踪的ValueError异常,浏览器收到一个空文档。
当包含"E2"时,会立即记录带有堆栈跟踪的ValueError异常,但浏览器会等待,直到反应堆关闭,此时它会收到一个空文档。
当包含"E3"时,立即记录带有堆栈跟踪的ValueError异常,浏览器等待直到反应堆关闭,并在该点接收预期的文档。
当包含raise语句"E4"时,将立即将预期文档返回到浏览器,并立即记录带有堆栈跟踪的ValueError异常。(在这种情况下是否有资源泄漏的可能性?)
解决方案
好吧,在看了几次你的问题后,我想我明白你的要求了。我还重新编写了你的代码,使你的原始答案更好一点。这个新的答案应该炫耀deferred's的所有权力。
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
此外,我建议您阅读 克隆多 教程。它会教你你需要知道的关于延迟的一切。
编辑:
已经修改了上面的代码来修复一些愚蠢的错误。也改进了它。如果异常发生在任何地方(除了在 self.errback
, ,但我们需要某种程度的信任)然后它将被传递给 self.errback
它将记录或打印twisted中的错误,然后将跟踪发送到浏览器 和 关闭请求。这应该可以阻止任何资源泄漏。
其他提示
我通过挖掘扭曲的来源来挖掘出来。必要的洞察力是反应堆和延期回调/静态链逻辑与请求对象解耦,这是数据如何返回浏览器。抖动是必要的,但不能仅仅在我发布的原始代码中将失败对象递减。错误必须将错误报告给浏览器。
以下代码符合我的要求(切勿保留浏览器等待,始终给出堆栈跟踪,不需要重新启动堆栈以再次进行操作),并将我允许我装饰阻止方法,从而将它们委托将它们委派以保持封锁反应器响应于其他事件(此类方法基本上将在这里取代回调1)。但是,我确实发现,在以下代码中,取消关注“E4”RAIND语句在后续浏览器请求上产生非常奇怪的行为(从先前请求返回到浏览器的部分数据;死锁)。
希望其他人会发现这是一个有用的延迟例子。
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
.