修改Python的pyxmpp2中的主循环使其提高性能


Posted in Python onApril 24, 2015

引子

之前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?只需在实例化Client时传入

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

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

epoll仅仅在Linux下支持

Python 相关文章推荐
从零学python系列之数据处理编程实例(一)
May 22 Python
Python基于回溯法子集树模板解决全排列问题示例
Sep 07 Python
django使用html模板减少代码代码解析
Dec 12 Python
flask中过滤器的使用详解
Aug 01 Python
python write无法写入文件的解决方法
Jan 23 Python
python格式化输出保留2位小数的实现方法
Jul 02 Python
使用Python刷淘宝喵币(低阶入门版)
Oct 30 Python
python numpy 矩阵堆叠实例
Jan 17 Python
tensorflow pb to tflite 精度下降详解
May 25 Python
通过python-pptx模块操作ppt文件的方法
Dec 26 Python
selenium+headless chrome爬虫的实现示例
Jan 08 Python
Python中threading库实现线程锁与释放锁
May 17 Python
Python的Tornado框架异步编程入门实例
Apr 24 #Python
使用Python的Tornado框架实现一个简单的WebQQ机器人
Apr 24 #Python
Python程序中使用SQLAlchemy时出现乱码的解决方案
Apr 24 #Python
简单说明Python中的装饰器的用法
Apr 24 #Python
使用基于Python的Tornado框架的HTTP客户端的教程
Apr 24 #Python
简单介绍Python的Tornado框架中的协程异步实现原理
Apr 23 #Python
解决Python中由于logging模块误用导致的内存泄露
Apr 23 #Python
You might like
php下MYSQL limit的优化
2008/01/10 PHP
PHP HTML代码串 截取实现代码
2009/06/29 PHP
微信开发之网页授权获取用户信息(二)
2016/01/08 PHP
php版微信公众平台接口开发之智能回复开发教程
2016/09/22 PHP
PHP中使用mpdf 导出PDF文件的实现方法
2018/10/22 PHP
passwordStrength 基于jquery的密码强度检测代码使用介绍
2011/10/08 Javascript
JS 精确统计网站访问量的实例代码
2013/07/05 Javascript
Jquery动态进行图片缩略的原理及实现
2013/08/13 Javascript
IE8下String的Trim()方法失效的解决方法
2013/11/08 Javascript
jquery1.10给新增元素绑定事件的方法
2014/03/06 Javascript
JavaScript实现的购物车效果可以运用在好多地方
2014/05/09 Javascript
详解参数传递四种形式
2015/07/21 Javascript
Javascript点击按钮随机改变数字与其颜色
2016/09/01 Javascript
JS实现图片放大缩小的方法
2017/02/15 Javascript
实例分析JS与Node.js中的事件循环
2017/12/12 Javascript
vue与原生app的对接交互的方法(混合开发)
2018/11/28 Javascript
Node.js API详解之 util模块用法实例分析
2020/05/09 Javascript
vue实现放大镜效果
2020/09/17 Javascript
详解Python中__str__和__repr__方法的区别
2015/04/17 Python
python3实现读取chrome浏览器cookie
2016/06/19 Python
python 3.5下xadmin的使用及修复源码bug
2017/05/10 Python
Python探索之Metaclass初步了解
2017/10/28 Python
Python中str.join()简单用法示例
2018/03/20 Python
Django渲染Markdown文章目录的方法示例
2019/01/02 Python
使用python itchat包爬取微信好友头像形成矩形头像集的方法
2019/02/21 Python
Python基于BeautifulSoup和requests实现的爬虫功能示例
2019/08/02 Python
简单的Python人脸识别系统
2020/07/14 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
中邮全球便购:中国邮政速递物流
2017/03/04 全球购物
英语专业毕业生自荐信范文
2013/12/31 职场文书
党员领导干部廉洁从政承诺书
2014/03/27 职场文书
安全协议书
2014/04/23 职场文书
产品委托授权书范本
2014/09/16 职场文书
入党积极分子半年考察意见
2015/06/02 职场文书
Nginx 负载均衡是什么以及该如何配置
2021/03/31 Servers
如何判断微信付款码和支付宝付款码
2021/04/01 PHP