修改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的几条建议分享
Feb 10 Python
分析并输出Python代码依赖的库的实现代码
Aug 09 Python
在类Unix系统上开始Python3编程入门
Aug 20 Python
对python中array.sum(axis=?)的用法介绍
Jun 28 Python
python中多层嵌套列表的拆分方法
Jul 02 Python
python try except 捕获所有异常的实例
Oct 18 Python
对python 操作solr索引数据的实例详解
Dec 07 Python
python接口自动化(十七)--Json 数据处理---一次爬坑记(详解)
Apr 18 Python
基于Python中isfile函数和isdir函数使用详解
Nov 29 Python
python生成并处理uuid的实现方式
Mar 03 Python
解决Python 异常TypeError: cannot concatenate 'str' and 'int' objects
Apr 08 Python
Python创建简单的神经网络实例讲解
Jan 04 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/10/09 PHP
Laravel 连接(Join)示例
2019/10/16 PHP
使用新的消息弹出框blackbirdjs
2008/10/16 Javascript
jquery中dom操作和事件的实例学习 仿yahoo邮箱登录框的提示效果
2011/11/30 Javascript
javascript实现json页面分页实例代码
2014/02/20 Javascript
Javascript的严格模式strict mode详细介绍
2014/06/06 Javascript
鼠标悬浮显示二级菜单效果的jquery实现
2014/10/29 Javascript
9款2014最热门jQuery实用特效推荐
2014/12/07 Javascript
13 款最热门的 jQuery 图像 360 度旋转插件推荐
2014/12/09 Javascript
js实现跨域的几种方法汇总(图片ping、JSONP和CORS)
2015/10/25 Javascript
JavaScript面试题(指针、帽子和女朋友)
2016/11/23 Javascript
浅谈javascript alert和confirm的美化
2016/12/15 Javascript
jquery UI Datepicker时间控件冲突问题解决
2016/12/16 Javascript
浅谈对Angular中的生命周期钩子的理解
2017/07/31 Javascript
jQuery Datatable 多个查询条件自定义提交事件(推荐)
2017/08/24 jQuery
vue开发拖拽进度条滑动组件
2019/09/21 Javascript
从Node.js事件触发器到Vue自定义事件的深入讲解
2020/06/26 Javascript
Python提取频域特征知识点浅析
2019/03/04 Python
python 画出使用分类器得到的决策边界
2019/08/21 Python
python set集合使用方法解析
2019/11/05 Python
在matplotlib中改变figure的布局和大小实例
2020/04/23 Python
Python中zipfile压缩文件模块的基本使用教程
2020/06/14 Python
意大利香水和彩妆护肤品购物网站:Ditano
2017/08/13 全球购物
写出二分查找算法的两种实现
2013/05/13 面试题
应聘收银员个人的求职信
2013/11/30 职场文书
一名毕业生的自我鉴定
2013/12/04 职场文书
房屋改造计划书
2014/01/10 职场文书
商场促销活动方案
2014/02/08 职场文书
创新型城市实施方案
2014/03/06 职场文书
经济管理自荐书
2014/06/09 职场文书
房屋出租委托书格式
2014/09/23 职场文书
2014年个人委托书范本
2014/10/13 职场文书
汽车4S店销售经理岗位职责
2015/04/02 职场文书
2015年超市工作总结范文
2015/05/26 职场文书
Python爬虫中urllib3与urllib的区别是什么
2021/07/21 Python
教你使用Python获取QQ音乐某个歌手的歌单
2022/04/03 Python