Domanda

Sto lottando per produrre lo stesso comportamento nel codice di servizio Web che utilizza oggetti differiti come nel codice che non lo fa. Il mio obiettivo è scrivere un decoratore che delega il trattamento di qualsiasi metodo (che è disaccoppiato da Twisted) al pool di filo contorto, in modo che il reattore non sia bloccato, senza modificare nessuno di questa semantica del metodo.

Quando un'istanza di classe ECHO di seguito è esposta come servizio Web, questo codice:

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
.

visualizzerà un documento HTML al browser quando vengono commentati tutti gli estratti conto di sollevamento e visualizzare una traccia di pila ben formattata (che è intrecciata per me) quando è inclusa l'istruzione Aulant Etichettata "E5". Questo è quello che voglio. Allo stesso modo, se non utilizzo gli oggetti differiti e posizionando tutto il comportamento da CallBack1 e CallBack2 in Render_get (), un'eccezione sollevata ovunque in render_get produrrà la traccia dello stack desiderata.

Sto cercando di scrivere il codice che risponderà immediatamente al browser, non causare perdite di risorse in rotelle e causare anche la traccia della pila del browser per essere visualizzata anche nei casi in cui una qualsiasi delle dichiarazioni aumenta "E1" a "E3" È incluso nel codice differito - anche se ovviamente capisco che la pilata si traccia sarà diversa. (Il caso "E4" non mi interessa tanto.) Dopo aver letto la documentazione contorta e altre domande su questo sito non sono sicuro di come raggiungere questo obiettivo. Avrei pensato che aggiungere un erroraggio dovrebbe facilitare questo, ma evidentemente no. Ci deve essere qualcosa sugli oggetti differiti e sullo stack contorto.web che non capisco.

Gli effetti sul registro I documento qui possono essere influenzati dal mio utilizzo del PythonloggandobServer per il ponte Twisted Logging sul modulo di registrazione standard.

Quando è incluso "E1", il browser attende finché il reattore non viene spento, a quel punto l'eccezione ValueError con la traccia dello stack è registrata e il browser riceve un documento vuoto.

Quando è incluso "E2", l'eccezione ValueError con la traccia dello stack viene registrata immediatamente, ma il browser attende fino a quando il reattore si spegne a quale punto riceve un documento vuoto.

Quando è incluso "E3", l'eccezione ValueError con la traccia dello stack viene registrata immediatamente, il browser attende fino a quando il reattore si spegne, e a quel punto riceve il documento previsto.

Quando è incluso la dichiarazione aumentata "E4", il documento previsto viene restituito immediatamente al browser, e l'eccezione ValueError con la traccia dello stack viene registrata immediatamente. (C'è qualche possibilità di perdita di risorse in questo caso?)

È stato utile?

Soluzione

OK, dopo aver letto la tua domanda più volte, penso di capire cosa stai chiedendo.Ho anche rielaborato il tuo codice per fare un po 'meglio della tua risposta originale.Questa nuova risposta dovrebbe mostrare tutti i poteri di differiti.

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
.

Inoltre consiglio di leggere il krondo tutorial.Ti insegnerà tutto ciò che devi sapere su differiti.

Modifica:

hanno modificato il codice sopra per fissare alcuni bug sciocchi.Anche migliorato.Se si verifica un'eccezione (tranne che in self.errback, ma abbiamo bisogno di un certo livello di fiducia), verrà passato a self.errback che registrerà o stampare l'errore in Twisted e quindi inviare la traccia al browser e Chiudere la richiesta.Questo dovrebbe interrompere le perdite di risorse.

Altri suggerimenti

L'ho capito scavando attraverso la sorgente contorta. L'intuizione necessaria è che il reattore e la logica della catena di richiamata differita / erroback è disaccoppiata dall'oggetto di richiesta, che è il modo in cui i dati tornano al browser. È necessaria il serrario, ma non può semplicemente propagare l'oggetto di errore lungo la catena come nel codice originale che ho pubblicato. Il serio deve riportare l'errore al browser.

Il seguente codice soddisfa i miei requisiti (non tientare mai il browser in attesa, dà sempre la traccia dello stack, non richiede un riavvio del reattore per ottenere le cose di nuovo) e mi consentirà di decorare i metodi di blocco e quindi delegarli per threads per mantenere Il reattore reattivo ad altri eventi (tali metodi prenderanno essenzialmente il luogo di callback1 qui). Tuttavia, ho scoperto che nel seguente codice, il decommento della dichiarazione di aumento "E4" produce un comportamento molto strano sulle successive richieste del browser (dati parziali da richieste precedenti restituite al browser; Deadlock).

Speriamo che altri troveranno questo è un utile esempio differito.

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
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top