修改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类和函数中使用静态变量的方法
May 09 Python
详解Python中映射类型(字典)操作符的概念和使用
Aug 19 Python
Python爬虫实现网页信息抓取功能示例【URL与正则模块】
May 18 Python
Odoo中如何生成唯一不重复的序列号详解
Feb 10 Python
python操作mysql代码总结
Jun 01 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
Jul 25 Python
利用python画出折线图
Jul 26 Python
Python多线程原理与用法详解
Aug 20 Python
Python3使用Matplotlib 绘制精美的数学函数图形
Apr 11 Python
Python3中函数参数传递方式实例详解
May 05 Python
python实现TCP文件传输
Mar 20 Python
python 实现IP子网计算
Feb 18 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 XML操作类DOMDocument
2009/12/16 PHP
PHP 获取文件权限函数介绍
2013/07/11 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十三)
2014/06/26 PHP
PHP使用JpGraph绘制折线图操作示例【附源码下载】
2019/10/18 PHP
javascript string字符串优化问题
2011/07/31 Javascript
使用 js+正则表达式为关键词添加链接
2014/11/11 Javascript
jQuery给动态添加的元素绑定事件的方法
2015/03/09 Javascript
每天一篇javascript学习小结(基础知识)
2015/11/10 Javascript
js跨域资源共享 基础篇
2016/07/02 Javascript
PhotoSwipe异步动态加载图片方法
2016/08/25 Javascript
Node.JS中快速扫描端口并发现局域网内的Web服务器地址(80)
2017/09/18 Javascript
垃圾回收器的相关知识点总结
2018/05/13 Javascript
vue 实现Web端的定位功能 获取经纬度
2019/08/08 Javascript
vue+eslint+vscode配置教程
2019/08/09 Javascript
vue.js 子组件无法获取父组件store值的解决方式
2019/11/08 Javascript
Vue学习笔记之计算属性与侦听器用法
2019/12/07 Javascript
js 数据类型判断的方法
2020/12/03 Javascript
详解Python之数据序列化(json、pickle、shelve)
2017/03/30 Python
深入理解python中sort()与sorted()的区别
2018/08/29 Python
Python多线程同步---文件读写控制方法
2019/02/12 Python
python os.fork() 循环输出方法
2019/08/08 Python
Python 单例设计模式用法实例分析
2019/09/23 Python
Python实现鼠标自动在屏幕上随机移动功能
2020/03/14 Python
python 可视化库PyG2Plot的使用
2021/01/21 Python
虚拟环境及venv和virtualenv的区别说明
2021/02/05 Python
HTML5实现简单图片上传所遇到的问题及解决办法
2016/01/20 HTML / CSS
美国最大的存储市场:SpareFoot
2018/07/23 全球购物
美国家居用品和厨具购物网站:DealsDot
2019/10/07 全球购物
struct与class的区别
2014/02/03 面试题
司法局火灾防控方案
2014/06/05 职场文书
机械机修工岗位职责
2014/08/03 职场文书
向国旗敬礼活动总结
2014/09/27 职场文书
工作时间擅自离岗检讨书
2014/10/24 职场文书
2014年技术员工作总结
2014/11/18 职场文书
婚礼男方父母答谢词
2015/09/29 职场文书
python第三方网页解析器 lxml 扩展库与 xpath 的使用方法
2021/04/06 Python