修改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转码问题的解决方法
Oct 07 Python
python使用xlrd模块读写Excel文件的方法
May 06 Python
python requests.post带head和body的实例
Jan 02 Python
Python PyCharm如何进行断点调试
Jul 05 Python
利用python list完成最简单的DB连接池方法
Aug 09 Python
Python基本语法之运算符功能与用法详解
Oct 22 Python
python+tifffile之tiff文件读写方式
Jan 13 Python
浅析python表达式4+0.5值的数据类型
Feb 26 Python
Python如何将将模块分割成多个文件
Aug 04 Python
python 如何停止一个死循环的线程
Nov 24 Python
浏览器常用基本操作之python3+selenium4自动化测试(基础篇3)
May 21 Python
用Python生成会跳舞的美女
Jan 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
基于magic_quotes_gpc与magic_quotes_runtime的区别与使用介绍
2013/04/22 PHP
PHP命名空间(namespace)的使用基础及示例
2014/08/18 PHP
PDO防注入原理分析以及使用PDO的注意事项总结
2014/10/23 PHP
php中smarty区域循环的方法
2015/06/11 PHP
php策略模式简单示例分析【区别于工厂模式】
2019/09/25 PHP
prototype Element学习笔记(篇一)
2008/10/26 Javascript
简单通用的JS滑动门代码
2008/12/19 Javascript
js 发个判断字符串是否为符合标准的函数
2009/04/27 Javascript
让IE8支持DOM 2(不用框架!)
2009/12/31 Javascript
c#+jquery实现获取radio和checkbox的值
2020/09/12 Javascript
JQuery中使用on方法绑定hover事件实例
2014/12/09 Javascript
javascript实现控制div颜色
2015/07/07 Javascript
个人网站留言页面(前端jQuery编写、后台php读写MySQL)
2016/05/03 Javascript
jQuery 获取select选中值及清除选中状态
2016/12/13 Javascript
ng-options和ng-checked在表单中的高级运用(推荐)
2017/01/21 Javascript
angular2中router路由跳转navigate的使用与刷新页面问题详解
2017/05/07 Javascript
bootstrap-table组合表头的实现方法
2017/09/07 Javascript
详谈commonjs模块与es6模块的区别
2017/10/18 Javascript
JS在if中的强制类型转换方式
2018/07/15 Javascript
webpack4 配置 ssr 环境遇到“document is not defined”
2019/10/24 Javascript
React实现全选功能
2020/08/25 Javascript
[15:23]教你分分钟做大人:虚空假面
2014/10/30 DOTA
python3.5仿微软计算器程序
2020/03/30 Python
pyQt4实现俄罗斯方块游戏
2018/06/26 Python
我喜欢你 抖音表白程序python版
2019/04/07 Python
详解Python的循环结构知识点
2019/05/20 Python
html5指南-3.如何实现html元素拖拽功能
2013/01/07 HTML / CSS
法国二手MacBook销售网站:Okamac
2019/03/18 全球购物
毕业生个人求职自荐信
2014/02/26 职场文书
学习之星事迹材料
2014/05/17 职场文书
2014国庆节商场促销活动策划方案
2014/09/16 职场文书
2014年教研室工作总结
2014/12/06 职场文书
小平小道观后感
2015/06/09 职场文书
高二化学教学反思
2016/02/22 职场文书
2016大学生诚信考试承诺书
2016/03/25 职场文书
mongoDB数据库索引快速入门指南
2022/03/23 MongoDB