修改Python的pyxmpp2中的主循環(huán)使其提高性能
引子
之前clubot使用的pyxmpp2的默認(rèn)mainloop也就是一個(gè)poll的主循環(huán),但是clubot上線后資源占用非常厲害,使用strace跟蹤發(fā)現(xiàn)clubot在不停的poll,查看pyxmpp2代碼發(fā)現(xiàn)pyxmpp2的poll在使用超時(shí)阻塞時(shí)使用最小超時(shí)時(shí)間,而最小超時(shí)時(shí)間一直是0,所以會(huì)變成一個(gè)沒有超時(shí)的非阻塞poll很浪費(fèi)資源,不打算更改庫代碼,所以自己仿照poll的mainloop寫了一個(gè)更加高效的epoll的mainloop
實(shí)現(xiàn)
#!/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 # 帶有超時(shí)的非阻塞,解約資源 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?只需在實(shí)例化Client時(shí)傳入
mainloop = EpollMainLoop(settings) client = Client(my_jid, [self, version_provider], settings, mainloop)
這樣就會(huì)使用epoll作為mainloop
注意
epoll僅僅在Linux下支持
相關(guān)文章
Python?pycryptodome庫實(shí)現(xiàn)RSA加密解密消息
本文為大家介紹了如何在?Python?中使用?RSA?公鑰加密技術(shù)來加密和解密消息,并使用?pycryptodome?庫進(jìn)行實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助2024-02-02教你怎么用PyCharm為同一服務(wù)器配置多個(gè)python解釋器
當(dāng)我們?cè)诜?wù)器上創(chuàng)建了多個(gè)虛擬環(huán)境時(shí),也可以在 PyCharm 中配置這些虛擬環(huán)境,方便不同的項(xiàng)目使用不同的環(huán)境,然而按照網(wǎng)上教程添加多個(gè)python解釋器后,PyCharm會(huì)自動(dòng)幫我們創(chuàng)建多個(gè)重復(fù)的服務(wù)器,本文主要給出該問題的解決方法,同時(shí)也對(duì)添加解釋器做一個(gè)詳細(xì)的講解2021-05-05python+requests接口壓力測(cè)試500次,查看響應(yīng)時(shí)間的實(shí)例
這篇文章主要介紹了python+requests接口壓力測(cè)試500次,查看響應(yīng)時(shí)間的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-04-04Python定時(shí)庫APScheduler的原理以及用法示例
APScheduler的全稱是Advanced Python Scheduler,它是一個(gè)輕量級(jí)的 Python 定時(shí)任務(wù)調(diào)度框架,下面這篇文章主要給大家介紹了關(guān)于Python定時(shí)庫APScheduler的原理以及用法的相關(guān)資料,需要的朋友可以參考下2021-12-12Python文件基本操作open函數(shù)應(yīng)用與示例詳解
這篇文章主要為大家介紹了Python文件基本操作open函數(shù)應(yīng)用與示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12python利用winreg生成桌面路徑及實(shí)現(xiàn)掃描二維碼圖片返回相關(guān)信息
這篇文章主要介紹了python生成桌面路徑及實(shí)現(xiàn)掃描二維碼圖片返回相關(guān)信息,winreg是python的一個(gè)標(biāo)準(zhǔn)庫,用來對(duì)windows注冊(cè)表的操作,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下2022-06-06Python虛擬環(huán)境virtualenv的安裝與使用詳解
virtualenv可以用來管理互不干擾的獨(dú)立python虛擬環(huán)境,在有些場(chǎng)景下非常有用,下面這篇文章主要給大家介紹了Python虛擬環(huán)境virtualenv安裝與使用的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-05-05pyspark 隨機(jī)森林的實(shí)現(xiàn)
這篇文章主要介紹了pyspark 隨機(jī)森林的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04