Pregunta

Estoy luchando para producir el mismo comportamiento en el código de servicio web que utiliza objetos diferidos como en el código que no lo hace. Mi objetivo es escribir un decorador que delegará el procesamiento de cualquier método (que se desacopla de retorcido) al grupo de hilos torcidos, de modo que el reactor no esté bloqueado, sin cambiar la semántica de ese método.

Cuando se expone una instancia de Clase ECHO a continuación como un servicio 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

mostrará un documento HTML al navegador cuando se comenten todas las declaraciones de aumento, y visualice una traza de pila bien formateada (que retorcido hace para mí) cuando se incluye la declaración de aumento etiquetada "E5". Eso es lo que quiero. Del mismo modo, si no uso objetos diferidos en absoluto y coloque todo el comportamiento de CallBack1 y CallBack2 en Render_Get (), una excepción recaudada en cualquier lugar dentro de Render_Get producirá la traza de la pila deseada.

Estoy tratando de escribir código que responderá inmediatamente al navegador, no causar fugas de recursos dentro de Twisted, y hacer que el seguimiento de la pila del navegador también se mostrara en los casos en los que cualquiera de las declaraciones de aumento "E1" a "E3" Está incluido en el código diferido, aunque, por supuesto, entiendo que los rastros de la pila serán diferentes. (El caso "E4", no me importa tanto). Después de leer la documentación torcida y otras preguntas en este sitio, no estoy seguro de cómo lograr esto. Habría pensado que agregar un error debería facilitar esto, pero evidentemente no. Debe haber algo acerca de los objetos diferidos y la pila Twisted.Web que no estoy entendiendo.

Los efectos en el registro I Aquí pueden verse afectados por mi uso del pythonloggingobserver para brillar el registro torcido en el módulo de registro estándar.

Cuando se incluye "E1", el navegador espera hasta que el reactor se apaga, momento en el que se registra la excepción de ValueError con STACK TRACK y el navegador recibe un documento vacío.

Cuando se incluye "E2", la excepción de ValueError con rastreo de pila se registra de inmediato, pero el navegador espera hasta que el reactor se apaga en qué punto recibe un documento vacío.

Cuando se incluye "E3", la excepción de ValueError con rastreo de pila se registra de inmediato, el navegador espera hasta que el reactor se apague, y en ese punto recibe el documento previsto.

Cuando se incluye la declaración "E4", el documento previsto se devuelve al navegador inmediatamente, y la excepción de ValueError con rastreo de pila se registra de inmediato. (¿Hay alguna posibilidad de una fuga de recursos en este caso?)

¿Fue útil?

Solución

OK, después de leer tu pregunta varias veces, creo que entiendo lo que pregunta.También me he vuelto a trabajar código para hacer un poco mejor que su respuesta original.Esta nueva respuesta debe mostrar todos los poderes de los diferidos.

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

También le recomiendo que lea el krondo tutorial.Te enseñará todo lo que necesitas saber sobre diferido.

Editar:

ha modificado el código de arriba para solucionar algunos errores tontos.También lo mejoró.Si una excepción ocurre en cualquier lugar (excepto en self.errback, pero necesitamos cierto nivel de confianza), se pasará al self.errback, que registrará o imprimirá el error en retorcido y luego enviará la traza al navegador y cerrar la solicitud.Esto debería detener cualquier fuga de recursos.

Otros consejos

Lo descubrí cavando a través de la fuente torcida. La información necesaria es que el reactor y la lógica de la cadena de devolución de devolución / de devolución de devoluciones diferidos se desacoplan del objeto de solicitud, que es la forma en que los datos vuelven al navegador. El error es necesario, pero no puede simplemente propagar el objeto de falla en la cadena como en el código original que publiqué. El error debe informar el error al navegador.

El siguiente código cumple con mis requisitos (nunca mantiene la espera del navegador, siempre le da a la traza de la pila, no requiere un reinicio del reactor para volver a hacer las cosas) y me permitirá decorar métodos de bloqueo y, por lo tanto, delegarlos a los hilos para mantener El reactor que responde a otros eventos (tales métodos esencialmente tomarán el lugar de la devolución de llamada1 aquí). Sin embargo, encontré que en el código de abajo, no comprometido la declaración de sensuales "E4" produce un comportamiento muy extraño en las solicitudes posteriores del navegador (datos parciales de las solicitudes anteriores devueltas al navegador; punto muerto).

Ojalá Otros encuentre que esto es un ejemplo diferido útil.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top