修改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 分析Nginx访问日志并保存到MySQL数据库实例
Mar 13 Python
Python3里的super()和__class__使用介绍
Apr 23 Python
在Python的Flask中使用WTForms表单框架的基础教程
Jun 07 Python
Python模拟三级菜单效果
Sep 11 Python
pandas数值计算与排序方法
Apr 12 Python
pandas 使用apply同时处理两列数据的方法
Apr 20 Python
python之文件读取一行一行的方法
Jul 12 Python
Python并行分布式框架Celery详解
Oct 15 Python
详解Python中namedtuple的使用
Apr 27 Python
Python如何绘制日历图和热力图
Aug 07 Python
Python虚拟环境virtualenv创建及使用过程图解
Dec 08 Python
Python爬取网站图片并保存的实现示例
Feb 26 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 字符过滤类,用于过滤各类用户输入的数据
2009/05/27 PHP
php数组函数序列之array_keys() - 获取数组键名
2011/10/30 PHP
PHP中file_exists与is_file,is_dir的区别介绍
2012/09/12 PHP
一个javascript参数的小问题
2008/03/02 Javascript
jquery获取焦点和失去焦点事件代码
2013/04/21 Javascript
JS.findElementById()使用介绍
2013/09/21 Javascript
原生Js实现简易烟花爆炸效果的方法
2015/03/20 Javascript
jQuery操作属性和样式详解
2016/04/13 Javascript
jQuery1.9+中删除了live以后的替代方法
2016/06/17 Javascript
JavaScript基础知识点归纳(推荐)
2016/07/09 Javascript
Angular 2父子组件之间共享服务通信的实现
2017/07/04 Javascript
javascript 取小数点后几位几种方法总结
2017/08/02 Javascript
深入理解vuex2.0 之 modules
2017/11/20 Javascript
angularJs在多个控制器中共享服务数据的方法
2018/09/30 Javascript
详解小程序如何避免多次点击,重复触发事件
2019/04/08 Javascript
vue + typescript + video.js实现 流媒体播放 视频监控功能
2019/07/07 Javascript
JavaScript常用进制转换及位运算实例解析
2020/10/14 Javascript
[58:42]DOTA2上海特级锦标赛C组败者赛 Newbee VS Archon第一局
2016/02/27 DOTA
对python创建及引用动态变量名的示例讲解
2018/11/10 Python
浅谈python编译pyc工程--导包问题解决
2019/03/20 Python
python pip源配置,pip配置文件存放位置的方法
2019/07/12 Python
关于python pycharm中输出的内容不全的解决办法
2020/01/10 Python
基于opencv的selenium滑动验证码的实现
2020/07/24 Python
给Django Admin添加验证码和多次登录尝试限制的实现
2020/07/26 Python
PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)的步骤详解
2020/11/02 Python
Servlet都有哪些方法?主要作用是什么?
2014/03/04 面试题
酒店采购员岗位职责
2014/03/14 职场文书
师德师风建设方案
2014/05/08 职场文书
给学校的建议书范文
2014/05/15 职场文书
北京青年观后感
2015/06/15 职场文书
莫言获奖感言(全文)
2015/07/31 职场文书
新郎新娘致辞
2015/07/31 职场文书
超市主管竞聘书
2015/09/15 职场文书
学校学习型党组织建设心得体会
2019/06/21 职场文书
入党转正申请自我鉴定
2019/06/25 职场文书
python 办公自动化——基于pyqt5和openpyxl统计符合要求的名单
2021/05/25 Python