并发编程利器Eventlet

时间:2021-09-24 21:02:06

Eventlet是由第二人生(Secondlife)开源的高度伸缩性的Python网络编程库.


根据官方介绍大致特性如下:

  • 非阻塞I/O模型
  • 协程(Coroutines)使得开发者可以采用阻塞式的开发风格,却能够实现非阻塞I/O的效果
  • 隐式事件调度,使得可以在Python解释器或者应用程序的某一部分去使用Eventlet


关于协程,大致可以理解成允许子程序可以多次暂停和恢复执行,是实现多任务的一种有效手段,具体见这里


在Python的世界里,实现了nonblocking I/O的产品并不算少.比如内置的Asyncore和著名的Twisted.相比之下,Eventlet是更容易上手和使用的。


举个例子


import eventlet

pool = eventlet.GreenPool()

while True:    pool.spawn(func,args)


上面这段代码,几乎就是使用eventlet的范式:

  • GreenPool 用来实现协程,保证并行;
  • Spawn     用来调用相应的函数,完成具体业务.

每个func之间切换,实施“你运行一会、我运行一会”,并且在进行切换时必须指定何时切换以及切换到哪,当出现阻塞时,就显式切换到另一 段没有被阻塞的代码段执行,直到原先的阻塞状况消失以后,再人工切换回原来的代码段继续处理.


Eventlet内置提供了一个基于上述原理实现的数据库连接池,目前仅支持MySQL和PostgreSQL.为了测试其性能如何,我参考了gashero的这篇文章,并简化了测试方案.


测试对象分别是MySQLdb(MySQL驱动的Python封装),Eventlet.db_pool,DBUtils


测试代码如下:

import time

import random

import MySQLdb

import eventlet.db_pool as db_pool

from DBUtils.PooledDB import PooledDB


conn_kwargs={'host':'192.168.8.84','user':'root','passwd':'','db':'logs'}

sql="""SELECT * FROM test WHERE id=%d"""

pooled=db_pool.ConnectionPool(MySQLdb,**conn_kwargs)

pooldb=PooledDB(MySQLdb,**conn_kwargs)


def query(conn):

    cur=conn.cursor()

    cur.execute(sql%(random.randint(1,1000)))

    data=cur.fetchall()

    return cur


def print_now():

    print time.strftime("%H:%M:%S")

    return


def test1(times):

    print_now()

    for i in range(0,times):

        conn=MySQLdb.connect(**conn_kwargs)

        r = query(conn)

        r.close()

        conn.close()

    print_now()

    return


def test2(times):

    print_now()

    for i in range(0,times):

        conn=pooled.get()

        try:

            query(conn)

        finally:

            pooled.put(conn)

    print_now()

    return


def test3(times):

    print_now()

    for i in range(0,times):

        conn=pooldb.connection()

        r=query(conn)

        r.close()

        conn.close()

    print_now()

    return


然后进入Python解释器交互环境

Python -i db-pool-test.py

>>> test1(10000) //MySQLdb

16:04:34

16:11:25

>>> test2(10000) //Event

16:12:35

16:15:22

>>> test3(10000) //DBUtils

16:15:28

16:18:09

 

总体来看,和传统的MySQLdb相比,性能有了很大的提升,和DBUtils差别并不是很明显.


协程凶猛啊!