修改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异步任务队列示例
Apr 01 Python
低版本中Python除法运算小技巧
Apr 05 Python
Python注释详解
Jun 01 Python
PyCharm 常用快捷键和设置方法
Dec 20 Python
Python实现备份MySQL数据库的方法示例
Jan 11 Python
Python实现的爬取百度文库功能示例
Feb 16 Python
Python基本数据结构与用法详解【列表、元组、集合、字典】
Mar 23 Python
Django REST framework 如何实现内置访问频率控制
Jul 23 Python
Python 实现OpenCV格式和PIL.Image格式互转
Jan 09 Python
django中嵌套的try-except实例
May 21 Python
在TensorFlow中实现矩阵维度扩展
May 22 Python
浅谈keras中的目标函数和优化函数MSE用法
Jun 10 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
FCKeditor的安装(PHP)
2007/01/13 PHP
谨慎使用PHP的引用原因分析
2012/09/06 PHP
php根据生日计算年龄的方法
2015/07/13 PHP
简要剖析PHP的Yii框架的组件化机制的基本知识
2016/03/17 PHP
php实现图片缩略图的方法
2016/03/29 PHP
PHP单例模式是什么 php实现单例模式的方法
2016/05/14 PHP
php时间函数用法分析
2016/05/28 PHP
PHP类与对象后期静态绑定操作实例详解
2018/12/20 PHP
js里的prototype使用示例
2010/11/19 Javascript
jquery 之 $().hover(func1, funct2)使用方法
2012/06/14 Javascript
基于Jquery实现的一个图片滚动切换
2012/06/21 Javascript
jquery放大镜效果超漂亮噢
2013/11/15 Javascript
jquery 获取dom固定元素 添加样式的简单实例
2014/02/04 Javascript
以Python代码实例展示kNN算法的实际运用
2015/10/26 Javascript
jquery点击改变class并toggle的实现代码
2016/05/15 Javascript
学习Bootstrap滚动监听 附调用方法
2016/07/02 Javascript
jQuery+ajax实现修改密码验证功能实例详解
2017/07/06 jQuery
python爬取安居客二手房网站数据(实例讲解)
2017/10/19 Javascript
vue的全局变量和全局拦截请求器的示例代码
2018/09/13 Javascript
Vue中Table组件Select的勾选和取消勾选事件详解
2019/03/19 Javascript
ES6 Array常用扩展的应用实例分析
2019/06/26 Javascript
echarts实现折线图的拖拽效果
2019/12/19 Javascript
如何使用three.js 制作一个三维的推箱子游戏
2020/07/29 Javascript
[01:09]模型精美,特效酷炫!TI9不朽宝藏Ⅰ鉴赏
2019/05/10 DOTA
Python(Django)项目与Apache的管理交互的方法
2018/05/16 Python
删除DataFrame中值全为NaN或者包含有NaN的列或行方法
2018/11/06 Python
PyQt5实现简单数据标注工具
2019/03/18 Python
详解CSS3中nth-child与nth-of-type的区别
2017/01/05 HTML / CSS
工厂保洁员岗位职责
2013/12/04 职场文书
韩国商务邀请函
2014/01/14 职场文书
《翻越远方的大山》教学反思
2014/04/13 职场文书
大班幼儿评语大全
2014/04/30 职场文书
实验室的标语
2014/06/20 职场文书
PyTorch 如何自动计算梯度
2021/05/23 Python
python基础之函数的定义和调用
2021/10/24 Python
解决WINDOWS电脑开机后桌面没有任何图标
2022/04/09 数码科技