修改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入门篇之面向对象
Oct 20 Python
视觉直观感受若干常用排序算法
Apr 13 Python
Python编程之变量赋值操作实例分析
Jul 24 Python
Python中的浮点数原理与运算分析
Oct 12 Python
Python实现通过继承覆盖方法示例
Jul 02 Python
Python使用一行代码获取上个月是几月
Aug 30 Python
解决PyCharm同目录下导入模块会报错的问题
Oct 13 Python
python 利用pywifi模块实现连接网络破解wifi密码实时监控网络
Sep 16 Python
基于python使用tibco ems代码实例
Dec 20 Python
python cv2在验证码识别中应用实例解析
Dec 25 Python
Python递归函数特点及原理解析
Mar 04 Python
请求模块urllib之PYTHON爬虫的基本使用
Apr 08 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去除字符串换行符示例分享
2014/02/13 PHP
yii分页组件用法实例分析
2015/12/28 PHP
php 魔术常量详解及实例代码
2016/12/04 PHP
PHP实现的装箱算法示例
2018/06/23 PHP
PHP实现简易计算器功能
2020/08/28 PHP
打开超链需要“确认”对话框的方法
2007/03/08 Javascript
jquery判断单个复选框是否被选中的代码
2009/09/03 Javascript
利用onresize使得div可以随着屏幕大小而自适应的代码
2010/01/15 Javascript
js 居中漂浮广告
2010/03/21 Javascript
qTip 基于JQuery的Tooltip插件[兼容性好]
2010/09/01 Javascript
用JS判别浏览器种类以及IE版本的几种方法小结
2011/08/02 Javascript
JSON格式的键盘编码对照表
2015/01/29 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
AngularJs上传前预览图片的实例代码
2017/01/20 Javascript
js实现下一页页码效果
2017/03/07 Javascript
用Nodejs搭建服务器访问html、css、JS等静态资源文件
2017/04/28 NodeJs
JS中的Replace()传入函数时的用法详解
2017/09/11 Javascript
解决vue多个路由共用一个页面的问题
2018/03/12 Javascript
Vue实现动态创建和删除数据的方法
2018/03/17 Javascript
vue 路由meta 设置导航隐藏与显示功能的示例代码
2020/09/04 Javascript
[02:59]2014DOTA2西雅图国际邀请赛 圆满落幕中国夺冠
2014/07/23 DOTA
python获取当前计算机cpu数量的方法
2015/04/18 Python
深入理解Python爬虫代理池服务
2018/02/28 Python
python flask实现分页的示例代码
2018/08/02 Python
pytorch实现用Resnet提取特征并保存为txt文件的方法
2019/08/20 Python
pytorch 移动端部署之helloworld的使用
2020/10/30 Python
Python爬虫分析微博热搜关键词的实现代码
2021/02/22 Python
Ivory Isle Designs美国/加拿大:婚礼和活动文具公司
2018/08/21 全球购物
小学体育教学反思
2014/01/31 职场文书
环境卫生标语
2014/06/09 职场文书
护理学院专科毕业生求职信
2014/06/28 职场文书
2015年个人剖析材料范文
2014/12/29 职场文书
2019年预备党员的思想汇报:加深对党的认知
2019/09/25 职场文书
微信小程序实现聊天室功能
2021/06/14 Javascript
深入浅析Django MTV模式
2021/09/04 Python
Android 中的类文件和类加载器详情
2022/06/05 Java/Android