Openstack基础知识Eventlet

 exentlet在openstack中的应用,看见前辈们总结的很好,对于小白来说而且学习起来不费劲,直接转载过来。同时也向作者表示感谢。 这是eventlet的官方网站:http://eventlet.net/

  Eventlet库在OpenStack服务中上镜率很高,尤其是在服务的多线程WSGI Server并发处理请求的情况下,深入了解eventlet库是很必要的。Eventlet库是高性能网络库,其主要依赖于两个关键的库:greenlet和select.epoll (或者epoll等类似的库)

(1)、greenlet库了解并发的基础,eventlet库简单的对其封装之后,就构成了GreenTread。
(2)、select库中的epoll则是其默认的网络通信模型。正由于这两个库的相对独立性。

1. 首先说一下什么是协程

协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈,局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。线程与协同程序的主要区别在于,一个具有多线程的程序可以同时运行几个线程,而协同程序却需要彼此协作地运行。就是说,一个具有多个协同程序的程序在任何时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显示地挂起时,它的执行才会暂停。

2. 协程有什么好处呢?

(1)每个coroutine有自己私有的stack及局部变量。

(2)同一时间只有一个coroutine在执行,无需对全局变量加锁。

(3)顺序可控,完全由程序控制执行的顺序。而通常的多线程一旦启动,它的运行时序是没法预测的,因此通常会给测试所有的情况带来困难。所以能用coroutine解决的场合应当优先使用coroutine。

总之,协程就是运行在一个线程内的伪并发方式,最终只有一个协程在运行,然后程序来控制执行的顺序。

程序案例:

import greenlet  
	  
def test1(n):  
	print "test1:",n  
	gr2.switch(32)  
	print "test1: over"  
	  
def test2(n):  
	print "test2:",n  
	gr1.switch(23)  
	print "test2: over"  
	  
greenlet = greenlet.greenlet  
current = greenlet.getcurrent()  
gr1 = greenlet(test1,current)  
gr2 = greenlet(test2,current)  
gr1.switch(2)  
执行结果:

Openstack基础知识Eventlet_第1张图片
整个程序首先创建两个协程,创建的过程传入了要执行的函数和父greenlet(可理解为main主程序),然后调用其中的一个协程的switch函数,并且传递参数进去,就开始执行test1,然后到了gr2.switch(32)语句,切换到test2函数来,最后又切换回去。最终test1运行结束,回到父greenlet中,执行结束。这个过程就是始终只有一个协程在运行,函数的执行流由程序自己来控制

3. eventlet是什么,它用来做什么的?

在eventlet里,把“协程”叫做greenthread(绿色线程)。eventlet是一个python高性能的网络库,而且可以通过绿色线程来实现并发,所谓并发,就是开启了多个greenthread,并且对这些greenthread进行管理,以实现非阻塞式的I/O。比如说用eventlet可以很方便的写一个性能很好的web服务器,或者是一个效率很高的网页爬虫,这都归功于eventlet绿色线程,以及对绿色线程的管理机制

4. 几个主要API的理解(eventlet官方文档详细注释)

(1)Greenthread Spawn(spawn,孵化的意思,即如何产生greenthread)

       主要有3个函数可以创建绿色线程

       1)spawn(func, *args, **kwargs):

           创建一个绿色线程去运行func这个函数,后面的参数是传递给这个函数的参数。返回值是一个eventlet.GreenThread对象,这个对象可以用来接受func函数运行的返回值。在绿色线程池还没有满的情况下,这个绿色线程一被创建就立刻被执行。

      2)spawn_n(func, *args, **kwargs):

          这个函数和spawn()类似,不同的就是它没有返回值,因而更加高效,这种特性,使它也有存在的价值。

      3)spawn_after(seconds, func, *args, **kwargs)

          这个函数和spawn()基本上一样,都有一样的返回值,不同的是它可以限定在什么时候执行这个绿色线程,即在seconds秒之后,启动这个绿色线程。

 

(2)Greenthread Control (绿色线程控制)

      1)sleep(seconds=0)

          中止当前的绿色线程,以允许其它的绿色线程执行。

      2)eventlet.GreenPool  (绿色线程池)

          这是一个类,在这个类中用set集合来容纳所创建的绿色线程,并且可以指定容纳线程的最大数量(默认是1000个),它的内部是用Semaphore和Event这两个类来对池进行控制的,这样就构成了线程池。其中,有几个比较重要的方法:

          running(self):返回当前池中的绿色线程数

          free():返回当前池中仍可容纳的绿色线程数

          spawn()、spawn_n():创建新的绿色线程

          starmap(self, function, iterable)和imap(self, function, *iterables):

           这两个函数和标准的库函数中的这两个函数实现的功能是一样的,所不同的是这里将这两个函数的执行放到了绿色线程中。前者实现的是从iterable中取出每一项作为function的参数来执行,后者则是分别从iterables中各取一项,作为function的参数去执行。注意使用imapstarmap的使用方法

如:imap(pow, (2,3,10), (5,2,3)) --> 32 9 1000

        starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000

      3)eventlet.GreenPile

           这也是一个类,而且是一个很有用的类,在它内部维护了一个GreenPool对象和一个Queue对象。这个GreenPool对象可以是从外部传递进来的,也可以是在类内部创建的,GreenPool对象主要是用来创建绿色线程的,即在GreenPile内部调用了GreenPool.spawn()方法。而Queue对象则是用来保存spawn()方法的返回值的,即Queue中保存的是GreenThread对象。并且它还实现了next()方法,也就意味着GreenPile对象具有了迭代器的性质。所以如果我们要对绿色线程的返回值进行操作的话,用这个类是再好不过的了。

      4)eventlet.Queue

   说到队列就不得不画个类图了,基类是LightQueue,它实现了大部分的队列的常用方法。它是用collections做为实现队列的基本数据结构的。而且这个LightQueue的实现,不单单实现了存取操作,我觉得在本质上它实现了一个生产者和消费者问题,定义了两个set()类型的成员变量putters和getters,前者用来存放在队列满时,被阻塞的绿色线程,后者用来存放当队列空时,被阻塞的绿色线程。类中的putting()和getting()方法就是分别得到被阻塞的绿色线程的数量。

   Queue继承了LightQueue,并且又增加了它自己的两个方法:task_done()和join()。task_done()是被消费者的绿色线程所调用的,表示在这个项上的所有工作都做完了,join()是阻塞,直到队列中所有的任务都完成。LifoQueue和PriorityQueue是存放数据的两种不同的方式。

Openstack基础知识Eventlet_第2张图片

(3)Network Convenience Functions(和网络相关的函数)

     这些函数定义在convenience.py文件中,对和socket相关的网络通信进行了包装,注意,这里用的socket是经过修改后的socket,以使它使用绿色线程,主要有以下一个函数:

      1)connect(addr, family=socket.AF_INET, bind=None)

           主要执行了以下几个步骤:新建了一个TCP类型的socket,绑定本地的ip和端口,和远程的地址进行连接。

     2)listen(addr,family=socket.AF_INET, backlog=50)

           过程和connect()类似,只是把connect()换成了listen(),backlog指定了最大的连接数量。

     3)serve(sock,handle, concurrency=1000)

          这个函数直接创建了一个socket服务器,在它内部创建了一个GreenPool对象,默认的最大绿色线程数是1000,然后是一个循环来接受连接,源码如下:

	def serve(sock, handle, concurrency=1000):  
	    pool = greenpool.GreenPool(concurrency)  
	    server_gt = greenthread.getcurrent()  
	   
	    while True:  
	        try:  
	            conn, addr = sock.accept()  
	            gt = pool.spawn(handle, conn, addr)  
	            gt.link(_stop_checker, server_gt, conn)  
	            conn, addr, gt = None, None, None  
	        except StopServe:  
	            return 

5. 举例:

有了上面的基础知识,官方的例子就比较容易理解了。

(1)官方上引以为傲的“网页爬虫”,用到了绿色线程池和imap()函数

urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
     "https://wiki.secondlife.com/w/images/secondlife.jpg",
     "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]

import eventlet
from eventlet.green import urllib2  

def fetch(url):
  print "opening", url
  body = urllib2.urlopen(url).read()
  print "done with", url
  return url, body

pool = eventlet.GreenPool(200) //绿色线程池对象
for url, body in pool.imap(fetch, urls): //通过绿色线程执行fetch函数, 返回的是迭代器对象 (元祖列表)
  print "got body from", url, "of length", len(body)

(2)socket服务器

import eventlet

def handle(fd):
    print "client connected"
    while True:
        # pass through every non-eof line
        x = fd.readline()
        if not x: break
        fd.write(x)
        fd.flush()
        print "echoed", x,
    print "client disconnected"

print "server socket listening on port 6000"
server = eventlet.listen(('0.0.0.0', 6000))
pool = eventlet.GreenPool()
while True:
    try:
        new_sock, address = server.accept()
        print "accepted", address
        pool.spawn_n(handle, new_sock.makefile('rw')) //创建绿色线程响应客户端的连接请求
    except (SystemExit, KeyboardInterrupt):
        break
(3)使用GreenPile的例子
import eventlet
from eventlet.green import socket

def geturl(url):
    c = socket.socket()
    ip = socket.gethostbyname(url)
    c.connect((ip, 80))
    print '%s connected' % url
    c.sendall('GET /\r\n\r\n')
    return c.recv(1024)

urls = ['www.google.com', 'www.yandex.ru', 'www.python.org']
pile = eventlet.GreenPile()
for x in urls:
    pile.spawn(geturl, x)

# note that the pile acts as a collection of return values from the functions
# if any exceptions are raised by the function they'll get raised here
for url, result in zip(urls, pile):
    print '%s: %s' % (url, repr(result)[:50])

跟着博客学习到这里,感觉基本上对协程、绿色线程、网络并发等基本有概念,结合官网上的例子可以加深对eventlet实现并发网络库的理解。如果又不懂的地方,可以阅读官网案例。

参考资料:

http://eventlet.net/doc/index.html

http://docs.python.org/library/itertools.html

http://timyang.net/lua/lua-coroutine/

你可能感兴趣的:(openstack组件研究,openstack,eventlet)