Torcida diferidos vs bloqueio em serviços web
-
29-10-2019 - |
Pergunta
Eu estou lutando para produzir o mesmo comportamento em serviço da web de código que usa Diferidos objetos como no código que não.Meu objetivo é escrever um profissional que irá delegar o processamento de qualquer método (que é dissociado da Torcida) para a Torcida pool de threads, de modo que o reator não é bloqueado, sem alterar o método da semântica.
Quando uma instância da classe echo abaixo é exposta como um serviço da web, este código:
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
vai exibir um documento HTML para o browser quando todas as levantar demonstrações são comentados, e apresentar uma bem formatada rastreamento de pilha (que Torcida não para mim) quando a levantar instrução rotulada "E5" está incluído.É isso que eu quero.Da mesma forma, se eu não usar o Diferidos os objetos e coloque todo o comportamento de callback1 e callback2 dentro render_GET(), uma exceção é levantada em qualquer lugar dentro render_GET vai produzir o desejado de rastreamento de pilha.
Eu estou tentando escrever código que irá responder para o navegador imediatamente, para não causar vazamentos de recurso no prazo de Torcida, e fazer com que o navegador rastreamento de pilha para também ser apresentada nos casos em que qualquer uma das levantar demonstrações "E1" a "E3" é incluído no diferido código-embora, claro, eu entendo que os rastreamentos de pilha si vai ser diferente.(O "E4" caso eu não me preocupo tanto.) Depois de ler a Torcida documentação e outras questões sobre este site eu estou certo de como conseguir isso.Eu teria pensado que a adição de um errback deve facilitar isso, mas, evidentemente, não.Há algo sobre Diferidos objetos e a torcida.pilha da web que eu não estou entendendo.
Os efeitos no log I documento aqui pode ser afetada pelo uso da PythonLoggingObserver a ponte Torcida do log para o padrão do módulo de registo.
Quando "E1" é incluído, o navegador aguarda até que o reator é desligado, no ponto em que a exceção ValueError com rastreamento de pilha é registrado e o navegador recebe um documento vazio.
Quando "E2" é incluído, a exceção ValueError com rastreamento de pilha é registada imediatamente, mas o navegador aguarda até que o reator é desligado em que ponto ele recebe um documento vazio.
Quando "E3" é incluído, a exceção ValueError com rastreamento de pilha é registada imediatamente, o navegador aguarda até que o reator é desligado, e em um ponto em que recebe o documento pretendido.
Quando levantar instrução "E4" é incluído, o documento pretendido for retornado para o navegador imediatamente, e a exceção ValueError com rastreamento de pilha é registada imediatamente.(Não há qualquer possibilidade de uma fuga de recursos, neste caso?)
Solução
Ok, depois de ler a sua pergunta várias vezes, eu acho que eu entendo o que o seu pedindo.Eu também reformulado o código para fazer um pouco melhor do que o original de resposta.Esta nova resposta deve mostrar todos os poderes dos créditos do.
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
Também recomendo que você leia o krondo tutorial.Ele irá te ensinar tudo que você precisa saber sobre diferidos.
Editar:
Ter modificado o código acima para correção de alguns erros bobos.Também melhorá-lo.Se uma exceção acontece em qualquer lugar (exceto em self.errback
, mas precisamos de algum nível de confiança), em seguida, ele será passado para self.errback
o que será de registo ou imprimir o erro na torcida e, em seguida, enviar o rastreamento para o navegador e fechar o pedido.Isso deve parar de quaisquer fugas de recursos.
Outras dicas
Eu descobri por escavando a Torcida de origem.O discernimento necessário é que o reator e Diferidos de retorno de chamada/errback cadeia lógica é dissociado do objeto do pedido, que é como os dados que recebe de volta para o navegador.O errback é necessário, mas não pode simplesmente propagar a Falha objeto para a cadeia como no original, o código que eu postei.O errback deve relatar o erro para o navegador.
O código abaixo, atende todas as minhas necessidades (nunca mantém o navegador espera, sempre dá o rastreamento de pilha, não necessita de um reator de reiniciar para começar as coisas de novo) e vai permitir-me para decorar o bloqueio de métodos e, assim, delegar a threads para manter o reator ágil para outros eventos (tais métodos serão, essencialmente, tomar o lugar de callback1 aqui).No entanto, eu achei que no código abaixo, descomentando a "E4" levantar a instrução produz muito estranho comportamento em futuras solicitações de navegador (dados parciais de pedidos anteriores retornado para o navegador;deadlock).
Esperemos que os outros vão achar que este é um útil Diferidos exemplo.
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