python asyncore 客户端连接后 CPU 使用率达 100%
我正在使用asyncore来实现一个发布-订阅的功能。我知道使用twisted或者ZMQ可能是更好的选择,但在这种情况下,我需要用纯Python来实现。当我在等待连接时,CPU的使用率大约是1%。但是一旦有客户端连接进来,CPU的使用率就会一下子飙升到100%。即使客户端断开连接后,使用率也没有下降。
这是我的服务器类:
class Host(asyncore.dispatcher):
log = logging.getLogger('Host')
def __init__(self, port=7655):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('0.0.0.0', port,))
self.listen(5)
self.clients = []
def handle_accept(self):
socket, addr = self.accept()
self.log.info("Aceepted client at {0}:{1}".format(addr[0], addr[1]))
self.clients.append(RemoteClient(self, socket, addr))
def broadcast(self, message):
self.log.info("Broadcasting message: {0}".format(message))
for client in self.clients:
client.message(message)
这是我的处理程序:
class RemoteClient(asyncore.dispatcher):
log = logging.getLogger('Host')
def __init__(self, host, socket, address):
asyncore.dispatcher.__init__(self, socket)
self.host = host
self.outbox = collections.deque()
def message(self, message):
self.outbox.append(message)
def handle_write(self):
if not self.outbox:
return
message = self.outbox.popleft()
if len(message) > MAX_MESSAGE_LENGTH:
raise ValueError('Message too long')
self.send(message)
def handle_close(self):
self.host.clients.remove(self)
self.log.info("Client removed from list")
self.close()
def handle_error(self):
self.log.error("Socket error")
我尝试过寻找解决方案,但似乎搞不清楚发生了什么。任何帮助都非常感谢!
1 个回答
6
解释
你的问题在于你没有重写方法 asyncore.dispatcher.writeable
,这个方法的默认实现是这样的:
def writable(self):
return True
这导致 asyncore.poll
在 while True
的循环中运行(这样会占用100%的CPU):
def poll(timeout=0.0, map=None):
if map is None:
map = socket_map
if map:
r = []; w = []; e = []
for fd, obj in map.items():
is_r = obj.readable()
is_w = obj.writable() # This is always true
if is_r:
r.append(fd)
if is_w:
w.append(fd)
if is_r or is_w:
e.append(fd) # This always happens
if [] == r == w == e:
time.sleep(timeout) # This never happens
return
try:
# Here, having w (as function parameter) set to non-empty value
# causes select to immediately return without waiting with w set
# just to your client
r, w, e = select.select(r, w, e, timeout)
解决方案
我觉得一个比较好的解决办法是用某种 threading.Event
机制重新实现 asyncore,这样可以强制等待可写的对象,不过到目前为止,这个方法对我有效:
# Add to RemoteClient
def writable(self):
''' It has point to call handle_write only when there's something in outbox
Having this method always returning true will cause 100% CPU usage
'''
return bool(self.outbox)
# When you start loop
asyncore.loop(timeout=5.0)
你也可以在 官方文档 的例子中看到重写的 writable()
方法。
我只是更喜欢能在30秒之前就结束等待。