2017-12-18 2 views
1

Ich bin mit Tornado Server, 4.4.2 und 5.9.0 PyPy und Python 2.7.13, auf Ubuntu 16.04.3 LTS gehostetTornado Server, der die meiste CPU verwendet, während Tornado-Socks und nur zwei Clients verwendet werden.

einen neuen Client anmeldet und eine neue Klasse erstellt wird und an die Steckdose, so kann Dialog aufrechterhalten werden. Ich verwende eine globale Clients [] Liste, um die Klassen zu enthalten. Anfangs Dialog wie folgt aussieht:

clients = [] 

class RegisterWebSocket(SockJSConnection): 
    # intialize the class and handle on-open (some things left out) 

    def on_open(self,info): 
     self.ipaddress = info.headers['X-Real-Ip'] 

    def on_message(self, data): 
     coinlist = [] 
     msg = json.loads(data) 
     if 'coinlist' in msg: 
      coinlist = msg['coinlist'] 
     if 'currency' in msg: 
      currency = msg['currency'] 
      tz = pendulum.timezone('America/New_York') 
      started = pendulum.now(tz).to_day_datetime_string() 
      ws = WebClientUpdater(self, self.clientid, coinlist,currency, 
       started, self.ipaddress) 
      clients.append(ws) 

Die ws Klasse ist unten dargestellt, und ich benutze einen Tornado periodiccallback die Kunden mit ihren spezifischen Informationen

class WebClientUpdater(SockJSConnection): 

    def __init__(self, ws,id, clist, currency, started, ipaddress): 
     super(WebClientUpdater,self).__init__(ws.session) 
     self.ws = ws 
     self.id = id 
     self.coinlist = clist 
     self.currency = currency 
     self.started = started 
     self.ipaddress = ipaddress 
     self.location = loc 
     self.loop = tornado.ioloop.PeriodicCallback(self.updateCoinList, 
        20000, io_loop=tornado.ioloop.IOLoop.instance())          
     self.loop.start() 
     self.send_msg('welcome '+ id) 

    def updateCoinList(self): 
     pdata = db.getPricesOfCoinsInCurrency(self.coinlist,self.currency) 
     self.send(dict(priceforcoins = pdata)) 

    def send_msg(self,msg): 
     self.send(msg) 

ich auch bei 60 Sekunden periodiccallback alle 20 Sekunden aktualisiert beginnen bei Starten, um die Clients auf geschlossene Verbindungen zu überwachen und sie aus der Client [] -Liste zu entfernen. Die habe ich auf der Startlinie intern eine def zu nennen wie

if __name__ == "__main__": 
    app = make_app() 
    app.listen(options.port) 
    ScheduleSocketCleaning() 

und

def ScheduleSocketCleaning(): 
    def cleanSocketHouse(): 
     print "checking sockets" 
     for x in clients: 
      if x.is_closed: 
       x = None 

    clients[:] = [y for y in clients if not y.is_closed ] 

    loop = tornado.ioloop.PeriodicCallback(cleanSocketHouse, 60000,        
      io_loop=tornado.ioloop.IOLoop.instance()) 
    loop.start() 

Wenn ich den Server mit TOP überwachen sehe ich, dass es verwendet 4% cpu typisch mit Ausbrüchen sofort nach 60+ , aber später, sagen wir, nach ein paar Stunden wird es in den 90% und bleibt dort.

Ich habe strace verwendet und ich sehe eine enorme Anzahl von Stat-Aufrufe für die gleichen Dateien mit Fehlern in der strace -c-Ansicht, aber ich kann keine Fehler in einer Textdatei mit -o trace.log finden. Wie kann ich diese Fehler finden?

Aber ich merke auch, dass die meiste Zeit in epoll_wait verbraucht wird.

% -Zeit

  • 41,61 0,068097 7 9484 epoll_wait
  • 26,65 0,043617 0 906154 2410 stat
  • 15,77 0,025811 0 524072
  • 10,90 0,017840 129 138 BRK
  • 2,41 0,003937 9 417 madvise gelesen
  • 2,04 0,003340 0 524072 lseek
  • 0,56 0,000923 3 298 sendto
  • 0,06 0,000098 0 23779 gettimeofday
  • 100,00 0,163663 1989527 2410 Gesamt oben

Hinweis 2410 Fehler.

Wenn ich den Strace Ausgabestrom mit beiliegendem pid sehen, ich sehe nur endlose Stat fordert die gleichen Dateien ..

mir jemand, wie besser zu debuggen diese Situation beraten? Mit nur zwei Clients und 20 Sekunden zwischen den Clientaktualisierungen würde ich erwarten, dass die CPU-Nutzung (es gibt keine anderen Benutzer der Site während dieser Prototyp-Phase) weniger als 1% oder ungefähr so ​​groß ist.

+0

Ich sollte auch hinzufügen, ich habe Nginx-Server vor Tornado Forwarding-Anfragen. Auch auf der Client-Seite habe ich das erneute Verbinden von Javascript, das Wiederverbinden durchführt, wenn die Verbindung geschlossen wird. Ich habe es auf korrektes Verhalten überwacht und es scheint zu funktionieren wie gewünscht. –

+0

Sie haben ein Speicherleck. Sie schließen PeriodicCallbacks nie. So erhöht sich die Anzahl der Timer und nach einiger Zeit haben Sie so viele, dass sie ganze CPU-Zeit benötigen. Dies wird auf "epoll_wait" gesehen, da dort die Planung tatsächlich stattfindet. – freakish

+0

@freakish Danke .. Wie sollte ich PeriodicCallbacks schließen? –

Antwort

0

Sie müssen PeriodicCallbacks schließen, sonst ist es ein Speicherleck. Sie tun dies, indem Sie einfach .close() auf ein PeriodicCallback-Objekt aufrufen.Eine Möglichkeit, das zu lösen ist in Ihrer regelmäßigen Reinigung Aufgabe:

def cleanSocketHouse(): 
    global clients 
    new_clients = [] 
    for client in clients: 
     if client.is_closed: 
      # I don't know why you call it loop, 
      # .timer would be more appropriate 
      client.loop.close() 
     else: 
      new_clients.append(client) 
    clients = new_clients 

Ich bin nicht sicher, wie genau .is_closed ist (einige Tests erforderlich). Der andere Weg ist, updateCoinList zu ändern. Die .send() Methode sollte fehlschlagen, wenn der Client nicht mehr verbunden ist, oder? Daher sollte try: except: den Trick:

def updateCoinList(self): 
    global clients 
    pdata = db.getPricesOfCoinsInCurrency(self.coinlist,self.currency) 
    try: 
     self.send(dict(priceforcoins = pdata)) 
    except Exception: 
     # log exception? 
     self.loop.close() 
     clients.remove(self) # you should probably use set instead of list 

Wenn ,send() eigentlich nicht (aus irgendeinem Grund, ich bin nicht so vertraut mit Tornado) scheitert dann halten Sie sich an die erste Lösung.

Verwandte Themen