引子

之前clubot使用的pyxmpp2的默认mainloop也就是一个poll的主循环,但是clubot上线后资源占用非常厉害,使用strace跟踪发现clubot在不停的poll,查看pyxmpp2代码发现pyxmpp2的poll在使用超时阻塞时使用最小超时时间,而最小超时时间一直是0,所以会变成一个没有超时的非阻塞poll很浪费资源,不打算更改库代码,所以自己仿照poll的mainloop写了一个更加高效的epoll的mainloop
实现

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
#  Author :  cold
#  E-mail :  wh_linux@126.com
#  Date  :  13/01/06 10:41:31
#  Desc  :  Clubot epoll mainloop
#
from __future__ import absolute_import, division

import select

from pyxmpp2.mainloop.interfaces import HandlerReady, PrepareAgain
from pyxmpp2.mainloop.base import MainLoopBase

from plugin.util import get_logger



class EpollMainLoop(MainLoopBase):
  """ Main event loop based on the epoll() syscall on Linux system """
  READ_ONLY = (select.EPOLLIN | select.EPOLLPRI | select.EPOLLHUP |
         select.EPOLLERR |select.EPOLLET)
  READ_WRITE = READ_ONLY | select.EPOLLOUT
  def __init__(self, settings = None, handlers= None):
    self.epoll = select.epoll()
    self._handlers = {}
    self._unprepared_handlers = {}
    self._timeout = None
    self._exists_fd = {}
    self.logger = get_logger()
    MainLoopBase.__init__(self, settings, handlers)

    return

  def _add_io_handler(self, handler):
    self._unprepared_handlers[handler] = None
    self._configure_io_handler(handler)

  def _configure_io_handler(self, handler):
    if self.check_events():
      return
    if handler in self._unprepared_handlers:
      old_fileno = self._unprepared_handlers[handler]
      prepared = self._prepare_io_handler(handler)
    else:
      old_fileno = None
      prepared = True
    fileno = handler.fileno()
    if old_fileno is not None and fileno != old_fileno:
      del self._handlers[old_fileno]
      self._exists.pop(old_fileno, None)
      self.epoll.unregister(old_fileno)
    if not prepared:
      self._unprepared_handlers[handler] = fileno

    if not fileno:
      return

    self._handlers[fileno] = handler
    events = 0
    if handler.is_readable():
      events |= self.READ_ONLY
    if handler.is_writable():
      events |= self.READ_WRITE

    if events:
      if fileno in self._exists_fd:
        self.epoll.modify(fileno, events)
      else:
        self._exists_fd.update({fileno:1})
        self.epoll.register(fileno, events)

  def _prepare_io_handler(self, handler):
    ret = handler.prepare()
    if isinstance(ret, HandlerReady):
      del self._unprepared_handlers[handler]
      prepared = True
    elif isinstance(ret, PrepareAgain):
      if ret.timeout is not None:
        if self._timeout is not None:
          self._timeout = min(self._timeout, ret.timeout)
        else:
          self._timeout = ret.timeout
      prepared = False
    else:
      raise TypeError("Unexpected result from prepare()")

    return prepared

  def _remove_io_handler(self, handler):
    if handler in self._unprepared_handlers:
      old_fileno = self._unprepared_handlers[handler]
      del self._unprepared_handlers[handler]
    else:
      old_fileno = handler.fileno()
    if old_fileno is not None:
      try:
        del self._handlers[old_fileno]
        self._exists.pop(old_fileno, None)
        self.epoll.unregister(old_fileno)
      except KeyError:
        pass

  def loop_iteration(self, timeout = 60):
    next_timeout, sources_handled = self._call_timeout_handlers()
    if self.check_events():
      return
    if self._quit:
      return sources_handled
    for handler in list(self._unprepared_handlers):
      self._configure_io_handler(handler)
    if self._timeout is not None:
      timeout = min(timeout, self._timeout)
    if next_timeout is not None:
      timeout = min(next_timeout, timeout)

    if timeout == 0:
      timeout += 1  # 带有超时的非阻塞,解约资源
    events = self.epoll.poll(timeout)
    for fd, flag in events:
      if flag & (select.EPOLLIN | select.EPOLLPRI | select.EPOLLET):
        self._handlers[fd].handle_read()
      if flag & (select.EPOLLOUT|select.EPOLLET):
        self._handlers[fd].handle_write()
      if flag & (select.EPOLLERR | select.EPOLLET):
        self._handlers[fd].handle_err()
      if flag & (select.EPOLLHUP | select.EPOLLET):
        self._handlers[fd].handle_hup()
      #if flag & select.EPOLLNVAL:
        #self._handlers[fd].handle_nval()

      sources_handled += 1
      self._configure_io_handler(self._handlers[fd])

    return sources_handled

使用

如何使用新的mainloop"htmlcode">

mainloop = EpollMainLoop(settings)
client = Client(my_jid, [self, version_provider], settings, mainloop)

这样就会使用epoll作为mainloop
注意

epoll仅仅在Linux下支持

华山资源网 Design By www.eoogi.com
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
华山资源网 Design By www.eoogi.com

《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线

暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。

艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。

《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。