修改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之有点简约的元组
Sep 24 Python
Python实现八大排序算法
Aug 13 Python
python实时分析日志的一个小脚本分享
May 07 Python
PyQt5每天必学之事件与信号
Apr 20 Python
opencv实现静态手势识别 opencv实现剪刀石头布游戏
Jan 22 Python
python3对拉勾数据进行可视化分析的方法详解
Apr 03 Python
Python2和Python3的共存和切换使用
Apr 12 Python
python matplotlib库绘制条形图练习题
Aug 10 Python
解决tensorflow添加ptb库的问题
Feb 10 Python
python使用for...else跳出双层嵌套循环的方法实例
May 17 Python
python日志通过不同的等级打印不同的颜色(示例代码)
Jan 13 Python
python绘制汉诺塔
Mar 01 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
PHPlet在Windows下的安装
2006/10/09 PHP
Symfony数据校验方法实例分析
2015/01/26 PHP
PHP统计数值数组中出现频率最多的10个数字的方法
2015/04/20 PHP
PHP实现的最大正向匹配算法示例
2017/12/19 PHP
php7 list()、session及其他模块的修改实例分析
2020/05/25 PHP
jQuery asp.net 用json格式返回自定义对象
2010/04/07 Javascript
jquery 实现表单验证功能代码(简洁)
2012/07/03 Javascript
js特殊字符转义介绍
2013/11/05 Javascript
完美解决linux下node.js全局模块找不到的情况
2018/05/16 Javascript
在 Vue.js中优雅地使用全局事件的方法
2019/02/01 Javascript
解决包含在label标签下的checkbox在ie8及以下版本点击事件无效果兼容的问题
2019/10/27 Javascript
详解vue中多个有顺序要求的异步操作处理
2019/10/29 Javascript
vue中 v-for循环的用法详解
2020/02/19 Javascript
ant-design-vue中tree增删改的操作方法
2020/11/03 Javascript
[59:07]海涛为你详解DOTA2新版本“贤哲秘契”
2014/11/22 DOTA
python实现博客文章爬虫示例
2014/02/26 Python
Python新手实现2048小游戏
2015/03/31 Python
Python导出数据到Excel可读取的CSV文件的方法
2015/05/12 Python
Python将多个list合并为1个list的方法
2018/06/27 Python
win7下python3.6安装配置方法图文教程
2018/07/31 Python
Python设计模式之工厂方法模式实例详解
2019/01/18 Python
Django单元测试中Fixtures用法详解
2020/02/25 Python
Jupyter Notebook 实现正常显示中文和负号
2020/04/24 Python
Python如何使用27行代码绘制星星图
2020/07/20 Python
python实现ping命令小程序
2020/12/28 Python
环卫工人先进事迹材料
2014/06/02 职场文书
个人委托书范本
2014/09/13 职场文书
领导班子四风问题个人对照检查材料
2014/10/04 职场文书
公务员检讨书
2014/11/01 职场文书
2015年世界艾滋病日活动总结
2015/03/24 职场文书
2015小学音乐教师个人工作总结
2015/07/21 职场文书
2016年学校“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书
Vue接口封装的完整步骤记录
2021/05/14 Vue.js
Python 读写 Matlab Mat 格式数据的操作
2021/05/19 Python
SQL基础查询和LINQ集成化查询
2022/01/18 MySQL
MySQL数据库实验之 触发器和存储过程
2022/06/21 MySQL