修改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中操作列表之list.extend()方法的使用
May 20 Python
Python 多线程实例详解
Mar 25 Python
Python机器学习之决策树算法实例详解
Dec 06 Python
python 对给定可迭代集合统计出现频率,并排序的方法
Oct 18 Python
Django实现跨域的2种方法
Jul 31 Python
django中media媒体路径设置的步骤
Nov 15 Python
Python中Subprocess的不同函数解析
Dec 10 Python
python字符串,元组,列表,字典互转代码实例详解
Feb 14 Python
Python程序慢的重要原因
Sep 04 Python
python利用platform模块获取系统信息
Oct 09 Python
Python实现中英文全文搜索的示例
Dec 04 Python
用python查找统一局域网下ip对应的mac地址
Jan 13 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 fsockopen写的HTTP下载的类
2007/02/22 PHP
php入门学习知识点七 PHP函数的基本应用
2011/07/14 PHP
对于PHP 5.4 你必须要知道的
2013/08/07 PHP
php实现过滤表单提交中html标签的方法
2014/10/17 PHP
PHP判断网络文件是否存在的方法
2015/03/12 PHP
JavaScript 的方法重载效果
2009/08/07 Javascript
javascript 拖动表格行实现代码
2011/05/05 Javascript
javascript中的toFixed固定小数位数 简单实例分享
2013/07/12 Javascript
推荐JavaScript实现继承的最佳方式
2014/11/11 Javascript
JS模式之单例模式基本用法
2015/06/30 Javascript
JavaScript模拟数组合并concat
2016/03/06 Javascript
React-Native做一个文本输入框组件的实现代码
2017/08/10 Javascript
jQuery实现页码跳转式动态数据分页
2017/12/31 jQuery
vue2中引用及使用 better-scroll的方法详解
2018/11/15 Javascript
微信小程序如何引用外部js,外部样式,公共页面模板
2019/07/23 Javascript
JS实现提示框跟随鼠标移动
2019/08/27 Javascript
js实现中文实时时钟
2020/01/15 Javascript
vue实现按钮切换图片
2021/01/20 Vue.js
[01:29:17]RNG vs Liquid 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.23
2019/09/05 DOTA
python 爬取微信文章
2016/01/30 Python
python 创建一个空dataframe 然后添加行数据的实例
2018/06/07 Python
详解python statistics模块及函数用法
2019/10/27 Python
使用Django搭建一个基金模拟交易系统教程
2019/11/18 Python
django商品分类及商品数据建模实例详解
2020/01/03 Python
vscode+PyQt5安装详解步骤
2020/08/12 Python
numpy中生成随机数的几种常用函数(小结)
2020/08/18 Python
西班牙香水和化妆品购物网站:Arenal Perfumerías
2019/03/01 全球购物
教育学专业毕业生的自我评价
2013/11/21 职场文书
区域销售经理职责
2013/12/22 职场文书
临床护士自荐信
2014/01/31 职场文书
优秀教师主要事迹
2014/02/01 职场文书
投标承诺函格式
2015/01/21 职场文书
房屋认购协议书
2015/01/29 职场文书
2015医院个人工作总结范文
2015/05/21 职场文书
2015年高二班主任工作总结
2015/05/25 职场文书
一封真诚的自荐信帮你赢得机会
2019/05/07 职场文书