修改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中xrange用法分析
Apr 15 Python
python实现神经网络感知器算法
Dec 20 Python
利用Pandas 创建空的DataFrame方法
Apr 08 Python
python3 selenium 切换窗口的几种方法小结
May 21 Python
python远程连接服务器MySQL数据库
Jul 02 Python
python 读取文件并替换字段的实例
Jul 12 Python
python使用__slots__让你的代码更加节省内存
Sep 05 Python
wxPython实现列表增删改查功能
Nov 19 Python
解决tensorflow由于未初始化变量而导致的错误问题
Jan 06 Python
Python socket连接中的粘包、精确传输问题实例分析
Mar 24 Python
详解Python为什么不用设计模式
Jun 24 Python
如何使用pdb进行Python调试
Jun 30 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
phpMyAdmin下载、安装和使用入门教程
2007/05/31 PHP
php遍历所有文件及文件夹的方法深入解析
2013/06/08 PHP
PHP中基本HTTP认证技巧分析
2015/03/16 PHP
php将一维数组转换为每3个连续值组成的二维数组
2016/05/06 PHP
PHP智能识别收货地址信息实例
2019/01/05 PHP
js 鼠标拖动对象 可让任何div实现拖动效果
2009/11/09 Javascript
JQuery触发radio或checkbox的change事件
2012/12/18 Javascript
Javascript实现获取窗口的大小和位置代码分享
2014/12/04 Javascript
JS设置cookie、读取cookie、删除cookie
2015/04/17 Javascript
JavaScript变量的作用域全解析
2015/08/14 Javascript
jQuery实现下滑菜单导航效果代码
2015/08/25 Javascript
jquery实现通用的内容渐显Tab选项卡效果
2015/09/07 Javascript
Node.js静态文件服务器改进版
2016/01/10 Javascript
javascript图片切换综合实例(循环切换、顺序切换)
2016/01/13 Javascript
jQuery循环遍历子节点并获取值的方法
2016/04/14 Javascript
AngularJS API之copy深拷贝详解及实例
2016/09/14 Javascript
validationEngine 表单验证插件使用实例代码
2017/06/15 Javascript
jq源码解析之绑在$,jQuery上面的方法(实例讲解)
2017/10/13 jQuery
vue 路由懒加载中给 Webpack Chunks 命名的方法
2020/04/24 Javascript
nuxt 服务器渲染动态设置 title和seo关键字的操作
2020/11/05 Javascript
JS addEventListener()和attachEvent()方法实现注册事件
2021/01/11 Javascript
[01:42]DOTA2 – 虚无之灵
2019/08/25 DOTA
python Django模板的使用方法(图文)
2013/11/04 Python
深入讲解Python函数中参数的使用及默认参数的陷阱
2016/03/13 Python
深入浅析python中的多进程、多线程、协程
2016/06/22 Python
Python基于回溯法子集树模板解决马踏棋盘问题示例
2017/09/11 Python
selenium + python 获取table数据的示例讲解
2018/10/13 Python
PyCharm License Activation激活码失效问题的解决方法(图文详解)
2020/03/12 Python
python中scipy.stats产生随机数实例讲解
2021/02/19 Python
HTML5计时器小例子
2013/10/15 HTML / CSS
Sperry官网:帆船鞋创始品牌
2016/09/07 全球购物
美国购买肉、鸭、家禽、鹅肝和熟食网站:D’Artagnan
2018/11/13 全球购物
饿了么订餐官网:外卖、网上订餐
2019/06/28 全球购物
医院搬迁方案
2014/06/14 职场文书
信用卡工作证明范本
2015/06/19 职场文书
Python opencv缺陷检测的实现及问题解决
2021/04/24 Python