修改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使用Berkeley DB数据库实例
Sep 26 Python
Python进程通信之匿名管道实例讲解
Apr 11 Python
python实现读取命令行参数的方法
May 22 Python
Python发送http请求解析返回json的实例
Mar 26 Python
解决Python3中的中文字符编码的问题
Jul 18 Python
详解Python中pandas的安装操作说明(傻瓜版)
Apr 08 Python
Python pandas DataFrame操作的实现代码
Jun 21 Python
基于Python函数和变量名解析
Jul 19 Python
浅谈python opencv对图像颜色通道进行加减操作溢出
Jun 03 Python
python中 _、__、__xx__()区别及使用场景
Jun 30 Python
Python reversed反转序列并生成可迭代对象
Oct 22 Python
Elasticsearch 批量操作
Apr 19 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 的几个配置文件函数
2006/12/21 PHP
PHP伪静态页面函数附使用方法
2008/06/20 PHP
PHP实现支持SSL连接的SMTP邮件发送类
2015/03/05 PHP
PHP中file_get_contents函数抓取https地址出错的解决方法(两种方法)
2015/09/22 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
点击下载链接 弹出页面实现代码
2009/10/01 Javascript
Extjs中使用extend(js继承) 的代码
2012/03/15 Javascript
js简单实现用户注册信息的校验代码
2013/11/15 Javascript
用js+iframe形成页面的一种遮罩效果的具体实现
2013/12/31 Javascript
JavaScript实现更改网页背景与字体颜色的方法
2015/02/02 Javascript
JS组件系列之Bootstrap Icon图标选择组件
2016/01/28 Javascript
JS实现的跨浏览器解析XML文件实例
2016/06/21 Javascript
基于js对象,操作属性、方法详解
2016/08/11 Javascript
聊聊JavaScript如何实现继承及特点
2017/04/07 Javascript
使用React手写一个对话框或模态框的方法示例
2019/04/25 Javascript
javascript实现的时间格式加8小时功能示例
2019/06/13 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
el-table树形表格表单验证(列表生成序号)
2020/05/31 Javascript
在vant中使用时间选择器和popup弹出层的操作
2020/11/04 Javascript
[03:48]显微镜下的DOTA2第四期——TP动作
2014/06/20 DOTA
[50:12]EG vs Fnatic 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python 安装virtualenv和virtualenvwrapper的方法
2017/01/13 Python
python 调用c语言函数的方法
2017/09/29 Python
利用django-suit模板添加自定义的菜单、页面及设置访问权限
2018/07/13 Python
python集合比较(交集,并集,差集)方法详解
2018/09/13 Python
解决pycharm回车之后不能换行或不能缩进的问题
2019/01/16 Python
Python检查ping终端的方法
2019/01/26 Python
python 进程的几种创建方式详解
2019/08/29 Python
Python中turtle库的使用实例
2019/09/09 Python
Python hashlib加密模块常用方法解析
2019/12/18 Python
Python图像处理二值化方法实例汇总
2020/07/24 Python
HTTP状态码详解
2021/03/18 杂记
不假外出检讨书
2014/01/27 职场文书
房地产广告词大全
2014/03/19 职场文书
支部书记四风对照材料
2014/08/28 职场文书
MybatisPlus EntityWrapper如何自定义SQL
2022/03/22 Java/Android