簡體   English   中英

Tornado Server使用大多數cpu,同時使用tornado-sockjs和僅兩個客戶端。

[英]Tornado Server using most of the cpu while using tornado-sockjs and only two clients.

我正在使用托管在Ubuntu 16.04.3 LTS上的Tornado Server,4.4.2和pypy 5.9.0和python 2.7.13

一個新的客戶端登錄並創建一個新的類並通過套接字,因此可以維護對話框。 我正在使用全局 clients []列表包含這些類。 初始對話框如下所示:

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)

ws類如下所示,我使用龍卷風周期回調以每20秒更新一次客戶端的特定信息

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)

我還從啟動時的60秒定期回調開始,以監視客戶端是否已關閉連接並將它們從client []列表中刪除。 我放在啟動行上以內部調用def

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

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()

如果我使用TOP監視服務器,我會看到它使用的CPU典型值為4%,立即達到60+,但是稍后,例如,幾個小時后,它變為90%,並保持不變。

我使用了strace,並且在同一文件上看到大量的Stat調用,但在strace -c視圖中顯示了錯誤,但是使用-o trace.log在文本文件中找不到任何錯誤。 如何找到這些錯誤?

但我也注意到,大部分時間都用在epoll_wait中。

%時間

  • 41.61 0.068097 7 9484 epoll_wait
  • 26.65 0.043617 0 906154 2410統計
  • 15.77 0.025811 0 524072已讀
  • 10.90 0.017840 129138 brk
  • 2.41 0.003937 9417瘋狂
  • 2.04 0.003340 0 524072 lseek
  • 0.56 0.000923 3298發送至
  • 0.06 0.000098 0 23779 gettimeofday
  • 100.00 0.163663 1989527 2410總計

注意上面的2410錯誤。

當我使用附加的pid查看strace輸出流時,我只會在同一文件上看到無休止的Stat調用。

有人可以建議我如何更好地調試這種情況嗎? 由於只有兩個客戶端,並且客戶端更新之間只有20秒的間隔,因此,我希望CPU使用率(在原型階段沒有其他用戶使用該站點)會少於1%左右。

您需要關閉PeriodicCallbacks,否則會導致內存泄漏。 您只需在PeriodicCallback對象上調用.close()即可。 一種解決方法是在定期清潔任務中:

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

我不確定.is_closed有多准確(需要進行一些測試)。 另一種方法是更改updateCoinList 當客戶端不再連接時, .send()方法應該失敗,對嗎? 因此, try: except:應該做到這一點:

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

如果,send()實際上沒有失敗(出於某種原因,我對龍卷風並不熟悉),那么請堅持第一個解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM