2012-10-24 10 views
10

Sto cercando di impostare i limiti di velocità per il download/caricamento dei file e ho trovato che twisted fornisce twisted.protocols.policies.ThrottlingFactory per gestire questo lavoro, ma non riesco a farlo bene. Ho impostato readLimit e writeLimit, ma il file viene ancora scaricato a una velocità massima. Che cosa sto facendo di sbagliato?Regolazione della larghezza di banda tramite Twisted

from twisted.protocols.basic import FileSender 
from twisted.protocols.policies import ThrottlingFactory 
from twisted.web import server, resource 
from twisted.internet import reactor 
import os 

class DownloadPage(resource.Resource): 
    isLeaf = True 

    def __init__(self, producer): 
     self.producer = producer 

    def render(self, request): 
     size = os.stat(somefile).st_size 
     request.setHeader('Content-Type', 'application/octet-stream') 
     request.setHeader('Content-Length', size) 
     request.setHeader('Content-Disposition', 'attachment; filename="' + somefile + '"') 
     request.setHeader('Accept-Ranges', 'bytes') 

     fp = open(somefile, 'rb') 
     d = self.producer.beginFileTransfer(fp, request) 

     def err(error): 
      print "error %s", error 

     def cbFinished(ignored): 
      fp.close() 
      request.finish() 
     d.addErrback(err).addCallback(cbFinished) 

     return server.NOT_DONE_YET 


producer = FileSender() 
root_resource = resource.Resource() 
root_resource.putChild('download', DownloadPage(producer)) 
site = server.Site(root_resource) 
tsite = ThrottlingFactory(site, readLimit=10000, writeLimit=10000) 
tsite.protocol.producer = producer 
reactor.listenTCP(8080, tsite) 
reactor.run() 

UPDATE

Così qualche tempo dopo l'eseguo:

2012-10-25 09:17:03+0600 [-] Unhandled Error 
Traceback (most recent call last): 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/application/app.py", line 402, in startReactor 
     self.config, oldstdout, oldstderr, self.profiler, reactor) 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/application/app.py", line 323, in runReactorWithLogging 
     reactor.run() 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/internet/base.py", line 1169, in run 
     self.mainLoop() 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/internet/base.py", line 1178, in mainLoop 
     self.runUntilCurrent() 
    --- <exception caught here> --- 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/internet/base.py", line 800, in runUntilCurrent 
     call.func(*call.args, **call.kw) 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 334, in unthrottleWrites 
     p.unthrottleWrites() 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 225, in unthrottleWrites 
     self.producer.resumeProducing() 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 919, in resumeProducing 
     self.consumer.unregisterProducer() 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/web/http.py", line 811, in unregisterProducer 
     self.transport.unregisterProducer() 
     File "/home/chambylov/environments/transfer/local/lib/python2.7/site-packages/twisted/protocols/policies.py", line 209, in unregisterProducer 
     del self.producer 
    exceptions.AttributeError: ThrottlingProtocol instance has no attribute 'producer' 

vedo che non dovrei assegnare produttore come so tsite.protocol.producer = producer, Sono nuovo di Contorto e non so come farlo in un altro modo.

+1

Guardando la sorgente c'è una linea, 'lo g.msg ("Throttling legge su% s"% self) "puoi verificare che questo sia stato registrato? – John

+0

non registra i metodi * throttleReads, ma funziona * throttleWrites: 'Scritture di limitazione su ' –

+0

Suppongo che devi inserire oggetto Throttling tra 'file' read e e 'render()', o forse meglio tra 'reactor' e l'istanza' render/DownloadPage'. In questo momento sembra che tu abbia passato 'producer' agli oggetti DownloadPage e Throttling, questo non sembra giusto. –

risposta

1

Every producer needs (eventually) to be registered with whatever you want to consume the data. Non vedo la registrazione succedere da nessuna parte qui. Forse questo è il problema che stai avendo?

Twisted è stato utilizzato in alcuni progetti di grandi dimensioni come Friendster, ma tutti i callback non si adattano bene al solito modo in cui scrivo in python (e ho una certa esperienza con la programmazione funzionale). Sono passato a gevent.

Se si lavora con le librerie di gevent, molti dettagli (callback/generatori che forniscono la funzionalità asincrona) sono astratti, in modo che in genere è possibile scappare semplicemente con il patch del codice e scriverlo nel solito oggetto stile orientato al quale sei abituato. Se stai lavorando a un progetto con qualcuno che non ha familiarità con un linguaggio callback-pesante come js/lisp, scommetto che apprezzeranno il gevent over twisted.

+0

Questa risposta sarebbe migliore (degno di un upvote) senza l'inutile cecchinaggio. Non si parla nemmeno di come gevent si occupa del controllo del flusso. – Glyph

+0

@Glyph mi dispiace davvero per quello - spero di aver rimosso l'oppressione. Tonnellate di rispetto per le (brillanti) persone che hanno sviluppato Twisted - non intendevo offendere. Forse solo alcune cicatrici residue della mia brutta esperienza. :) – egbutter

+0

Grazie per averlo modificato, ma non si spiega ancora come gevent si occupi effettivamente del problema specifico del controllo di flusso :). – Glyph

1

Come indicato da egbutter, è necessario registrare un produttore. Così, invece di questo:

tsite.protocol.producer = producer 

dovete chiamare un metodo registerProducer esplicitamente:

tsite.protocol.registerProducer(...) 

o, se si sta utilizzando FileSender come produttore, chiamare il suo metodo beginFileTransfer , nel nostro caso:

file_to_send = open(...) 
producer.beginFileTransfer(file_to_send, tsite.protocol)