我正在使用Tornado Server,4.4.2和pypy 5.9.0以及python 2.7.13, 在Ubuntu 16.04.3 LTS上托管
新客户端登录并创建新类并传递套接字,因此可以维护对话框。我正在使用全局 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类如下所示,我使用tornado periodiccallback每隔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秒的周期回调开始,监控客户端的已关闭连接并将其从客户端[]列表中删除。我把它放在启动线上以在内部调用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监控服务器,我会看到它使用4%cpu,典型的爆发时间为60+,但稍后会说,几小时后它变为90%并保持在那里。
我已经使用了strace,我在strace -c视图中显示错误的相同文件上看到了大量的Stat调用,但是使用-o trace.log在文本文件中找不到任何错误。我怎样才能找到这些错误?
但我也注意到大部分时间都是在epoll_wait中消耗的。
%时间
注意上面的2410错误。
当我使用附加的pid查看strace输出流时,我只看到对相同文件的无限Stat调用..
有人可以告诉我如何更好地调试这种情况吗?只有两个客户端和客户端更新之间的20秒,我预计CPU使用率(在此原型阶段没有其他用户)将小于1%左右。
答案 0 :(得分:0)
您需要关闭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()
实际上没有失败(无论出于什么原因,我对Tornado并不熟悉),那么坚持第一个解决方案。