修改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中的推导式使用详解
Jun 03 Python
Python计算一个文件里字数的方法
Jun 15 Python
Python根据区号生成手机号码的方法
Jul 08 Python
浅析Python中MySQLdb的事务处理功能
Sep 21 Python
Python字符串处理实例详解
May 18 Python
Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例
Aug 07 Python
flask session组件的使用示例
Dec 25 Python
python3+selenium实现qq邮箱登陆并发送邮件功能
Jan 23 Python
django框架实现模板中获取request 的各种信息示例
Jul 01 Python
python实现双色球随机选号
Jan 01 Python
Python中免验证跳转到内容页的实例代码
Oct 23 Python
Python实现灰色关联分析与结果可视化的详细代码
Mar 25 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
亚洲咖啡有什么?亚洲咖啡产地介绍 亚洲咖啡有什么特点?
2021/03/05 新手入门
html中select语句读取mysql表中内容
2006/10/09 PHP
php实现高效获取图片尺寸的方法
2014/12/12 PHP
CakePHP框架Model函数定义方法示例
2017/08/04 PHP
javascript 常用关键字列表集合
2007/12/04 Javascript
当滚动条滚动到页面底部自动加载增加内容的js代码
2014/05/13 Javascript
JavaScript基本类型值-Undefined、Null、Boolean
2017/02/23 Javascript
了解VUE的render函数的使用
2017/06/08 Javascript
jquery实现企业定位式导航效果
2018/01/01 jQuery
vue.js过滤器+ajax实现事件监听及后台php数据交互实例
2018/05/22 Javascript
理顺8个版本vue的区别(小结)
2018/09/17 Javascript
微信小程序实现下拉菜单切换效果
2020/03/30 Javascript
详解vue中axios请求的封装
2019/04/08 Javascript
解决vue打包后刷新页面报错:Unexpected token
2019/08/27 Javascript
如何解决jQuery 和其他JS库的冲突
2020/06/22 jQuery
Python程序设计入门(5)类的使用简介
2014/06/16 Python
python通过加号运算符操作列表的方法
2015/07/28 Python
python字典快速保存于读取的方法
2018/03/23 Python
完美解决安装完tensorflow后pip无法使用的问题
2018/06/11 Python
python面向对象入门教程之从代码复用开始(一)
2018/12/11 Python
python二进制读写及特殊码同步实现详解
2019/10/11 Python
Python如何使用turtle库绘制图形
2020/02/26 Python
基于Python编写一个计算器程序,实现简单的加减乘除和取余二元运算
2020/08/05 Python
python爬虫爬取某网站视频的示例代码
2021/02/20 Python
一款纯css3实现的圆形旋转分享按钮旋转角度可自己调整
2014/09/02 HTML / CSS
纯CSS3代码实现文字描边
2016/04/25 HTML / CSS
分享CSS3制作卡片式图片的方法
2016/07/08 HTML / CSS
Nike台湾官方商店:Nike.com (TW)
2017/08/16 全球购物
Champion澳大利亚官网:美国冠军运动服装
2018/05/07 全球购物
匈牙利最大的健身制造商和销售商:inSPORTline
2018/10/30 全球购物
公共汽车、火车和飞机票的通用在线预订和销售平台:INFOBUS
2019/11/30 全球购物
Shell脚本如何向终端输出信息
2014/04/25 面试题
竞选副班长演讲稿
2014/04/24 职场文书
2015年老干部工作总结
2015/04/23 职场文书
《梅花魂》教学反思
2016/02/18 职场文书
导游词之桂林
2019/08/20 职场文书