gevent

gevent基于协程的网络库,基于libev的快速的事件循环,基于greenlet的轻量级执行单元,重用了Python标准库中的概念,支持socket、ssl、三方库通过打猴子补丁的形式来提供同步方式编写代码的异步支持,dns解析通过线程池或c-ares,内置的tcp udp http服务器,支持多进程,线程池。

gevent和eventlet

2009年当时eventlet不支持livevent
当时eventlet的monkey-patch有bug导致socket操作会挂起
its Hub API is geared towards pure Python event loops. Making it work smoothly would require significant changes in the interface and implementation of every other hub. The socket module bugs were also specific to event loop, so even though I fixed them for pyevent, they were still present in every other hub.

gevent在libevent的基础上

当然最新的实现是使用了libev

libevent是事件循环,使用epoll、kqueue,dns是异步的,同时它提供了http服务器。而eventlet则是维持的纯python的事件循环(最近才支持epoll)。

除了效率,还集成了信号处理,其他使用libevent的库也可以集成进来,dns解析也可以异步了,wsgi服务器也是在libevent的自带服务器上运行的(很快)

和python标准的风格相同

例如线程的事件等都一样

不同点

没有eventlet.db_pool
没有多进程eventlet.processes
如果是在twister的reactor上使用

事件循环

gevent告诉操作系统等数据到达的时候来通知它,然后gevent就可以继续处理其他的协程,例如已经获得了数据的。

不像其他的网络库,gevent在一个协程中隐式启动循环,不用调用run或者dispatch什么的,当它要阻塞的时候,就会获取hub实例并切换到其中,也就是把控制权交给了hub,如果此时还没有hub就会自动创建一个。

切换规则

注意只有一个greenlet放弃了控制才能执行其他的(调用阻塞函数会切换回hub),这对于io频繁的系统当然没问题,但是对于cpu频繁的系统、或者是调用一些能绕过libev的函数。

锁、信号量什么的其实没什么用了,而event、asyncresult、queue等则还是有用的。

使用

创建一个Greenlet然后调用它的start函数,或者直接调用spawn函数。然后在当前协程放弃控制后会切换到并执行。如果greenlet执行时触发了异常,并不会超出greenlet的界限,只是打印stacktrace 默认是到stderror。

join等待该greenlet退出,kill中断该greenlet执行,get获得返回的值或re raise它产生的异常。

可以派生Greenlet,更改它的str方法来改变显示的traceback信息,但是得在初始化函数中先调用Greenlet.init(self)。

class MyNoopGreenlet(Greenlet):

    def __init__(self, seconds):
Greenlet.__init__(self)
self.seconds = seconds def _run(self):
gevent.sleep(self.seconds) def __str__(self):
return 'MyNoopGreenlet(%s)' % self.seconds

kill可附带一个自定义的异常,但是可能该协程会捕获这个异常,可以附带timeout参数,也可以直接指定block=False来异步执行。

 

关于gevent的一点总结

0x00 基本概念

gevent是基于libev和greenlet的一个python异步框架。

libev是一个高性能的事件循环(event loop)实现。
事件循环(也称作IO多路复用),是解决阻塞问题,实现并发的一种方法。简单点说,就是event loop会捕获、处理io事件的变化:遇到阻塞,就跳出;阻塞结束,就继续。这依赖于系统底层的select函数及其升级版:pollepoll。《深入理解计算机系统》一书中,对此有深入探讨。

greenlet是一个python的协程管理、切换模块。通过greenlet,你可以显式地在不同的任务之间切换。

0x01 Actor模式

ActorErlang语言的精髓。它强调基于消息传递的并发机制。
我们模仿这篇文章 建立一个actor模型。核心代码如下:

import genvet
# 主队列
queue = gevent.queue.JoinableQueue()
while True:
# 持续获取数据
try:
data = queue.get(timeout=5)
except Empty:
gevent.sleep(0.5)
continue
# 交付给Actor
if data:
gl = Actor(data)
g1.start()
g1.join()

Actor为我们自定义的一个Greenlet的子类,核心代码如下:

class Actor(Greenlet):
def __init__(self, data):
self.data = data
Greenlet.__init__(self) def _run(self):
result = do_something(self.data)
return result

这样,我们这个Actor可以从消息队列中接受数据,通过start()_run()被调用。

由于geventmonkey patch的存在,你基本可以以同步的方式,写出异步的代码。这样的写法显得很多余。但是,却可以很好的实现业务的分离,让代码更清晰,更容易维护和扩展。

0x02 实现回调

如果你还希望给Actor加一个回调。即等他完成了之后,再进行某些处理。
那我们可以对Actor机型如下修改:

class Actor(Greenlet):
def __init__(self, data):
self.data = data
Greenlet.__init__(self) def _run(self):
# 通过self.link添加回调函数。
self.link(callback)
result = do_something(self.data)
return result

你可以通过Greenlet().link(),给你的协程添加一个回调。这个回调函数只接收一个参数,即这个协程的实例(g1)。我们会看到_run()函数,其实是返回了一个结果result的。那我们在回调函数中能否取得这个值呢。其实是可以的,这个值被存在了g1.value中。

还是那句话,这样的设计,可能看起来很多余。但随着框架功能的增加,这样多余的设计会让你的代码,愈发地灵活。

学Python不得不掌握的库,gevent和asyncio使用方法详解

 

一、gevent

python程序实现的一种单线程下的多任务执行调度器,简单来说在一个线程里,先后执行AB两个任务,但是当A遇到耗时操作(网络等待、文件读写等),这个时候gevent会让A继续执行,但是同时也会开始执行B任务,如果B在遇到耗时操作同时A又执行完了耗时操作,gevent又继续执行A。

使用示例:

其中gevent.sleep()是gevent自带的延时,当gevent遇到这个延时时会自动切换,这里用gevent.sleep()代替一些耗时操作,如数据库的读写、http请求、文件度写等操作,gevent给我们提供了一个很简单的协程操作。

二、asyncio

asyncio的使用上,感觉和gevent有异曲同工之妙

1、基础概念:

event_loop事件循环:理解为一个循环的池,里面存放一些async关键词定义的协程函数,只有放到循环池里才能执行

coroutine协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。

task任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。

future:代表将来执行或没有执行的任务的结果。它和task上没有本质的区别

async/await关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。

2、创建task

协程对象不能直接运行,需要包装成任务才能运行,上面是通过run_until_complete()方法包装成task(隐式包装),还有下面两种方式进行显式包装:

创建task后,task在加入事件循环之前是pending状态,加入loop后运行中是running状态,loop调用完是Done,运行完是finished状态,虽说本质上协程函数和task指的东西都一样,但是task有了协程函数的状态。

其中loop.run_until_complete()接受一个future参数,futurn具体指代一个协程函数,而task是future的子类,所以我们不声明一个task直接传入协程函数也能执行。

3、绑定回调函数

通过task的task.add_done_callback(callback)方法绑定回调函数,回调函数接收一个future对象参数如task,在内部通过future.result()获得协程函数的返回值。

4、await(挂起耗时操作)

多任务声明了协程函数,也同时在loop中注册了,他的执行也是顺序执行的,因为在异步函数中没有声明那些操作是耗时操作,所以会顺序执行。await的作用就是告诉控制器这个步骤是耗时的,async可以定义协程对象,使用await可以针对耗时的操作进行挂起

上面执行并不是异步执行,而是顺序执行,但是改成下面形式那就是异步执行:

可见三个任务的间隔时间几乎忽略不计,这里要注意可以使用await成功挂起的对应应该是下面三种:

原生异步函数(coroutine)

由 types.coroutine() 修饰的生成器,这个生成器可以返回 coroutine 对象。

包含 __await 方法的对象返回的一个迭代器

所以即使使用saync修饰requests的方法也不支持异步,而是需要专门的异步网络请求库aiohttp。

5、aiohttp

aiohttp需要单独安装,然后和asyncio库一起使用,看一下案例

几个任务的时间之差基本忽略不计,那亲测发送一千个请求也就11秒完成,确实很给力。

6、多进程配合使用

asyncio、aiohttp需要配合aiomultiprocess库使用,版本要求至少3.6,贴上该库的github上的使用示例,目前还在验证:

7、多协程并发

使用loop.run_until_complete(syncio.wait(tasks)) 也可以使用 loop.run_until_complete(asyncio.gather(*tasks)) ,前者传入task列表,会对task进行解包操作。

7、协程嵌套

顾名思义是一个协程中调用另一个协程,但是涉及到两个协程函数的结果处理和返回。

被调用协程返回结果有下列三种方式;

8、停止协程任务

实现结束task有两种方式:关闭单个task、关闭loop,涉及主要函数:

asyncio.Task.all_tasks()获取事件循环任务列表

KeyboardInterrupt捕获停止异常(Ctrl+C)

loop.stop()停止任务循环

task.cancel()取消单个任务

loop.run_forever()

loop.close()关闭事件循环,不然会重启

PYTHON GEVENT重要

gevent学习的更多相关文章

  1. python 协程库gevent学习--gevent数据结构及实战(四)

    一不留神已经到第四部分了,这一部分继续总结数据结构和常用的gevent类,废话不多说继续. 1.Timeout错误类 晚上在调试调用第三方接口的时候,发现有些接口耗时非常多,觉得应该有个超时接口来限制 ...

  2. python 协程库gevent学习--gevent数据结构及实战(三)

    gevent学习系列第三章,前面两章分析了大量常用几个函数的源码以及实现原理.这一章重点偏向实战了,按照官方给出的gevent学习指南,我将依次分析官方给出的7个数据结构.以及给出几个相应使用他们的例 ...

  3. python 协程库gevent学习--gevent源码学习(二)

    在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问: 1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用. 2. 关于在使用mon ...

  4. python 协程库gevent学习--源码学习(一)

    总算还是要来梳理一下这几天深入研究之后学习到的东西了. 这几天一直在看以前跟jd对接的项目写的那个gevent代码.为了查错,基本上深入浅出了一次gevent几个重要部件的实现和其工作的原理. 这里用 ...

  5. 【python】gevent学习

    之前测试了stackless,感觉不太好. 不过python作为最火的脚本语言,还是吸引力难挡. python的协程方案,除了stackless,还有greenlet, 相应的事件框架也有gevent ...

  6. Python gevent学习笔记-2

    在上一篇里面介绍了gevent的最主要的功能,先来来了解一下gevent里面一些更加高级的功能. 事件 事件是一种可以让greenlet进行异步通信的手段. ? 1 2 3 4 5 6 7 8 9 1 ...

  7. Python gevent学习笔记

    gevent是Python的一个用于网络IO的函数库,其中应用到了 coroutine(协同程序) 的思想.首先来了解下目前网络框架的几种基本的网络I/O模型: 阻塞式单线程:这是最基本的I/O模型, ...

  8. 协程gevent学习

    import gevent def f1(): print(11) gevent.sleep(2) print(33) def f2(): print(22) gevent.sleep(1) prin ...

  9. flask 实现异步非阻塞----gevent

    我们都知道,flask不支持异步非阻塞的请求,我们可以创建一个新项目去测试一下,推荐大家使用pycharm去开发我们的flask  使用特别的方便. rom flask import Flask im ...

随机推荐

  1. CentOS7下编译安装Python3.7.x【亲测有效】

    所有操作都在root用户下操作 下载安装包 编译安装 建立软链接 验证 安装: 更新yum: yum update 安装Python依赖: yum install openssl-devel bzip ...

  2. SpringBoot下,@WebFilter配置获取日志

    CREATE TABLE [dbo].[SWEBSERVICELOG]( [WLG_ID] [varchar](100) NOT NULL, [WLG_SESSIONID] [varchar](100 ...

  3. jsx的本质

    jsx语法 1.所有html标签他都支持        <div></div> 2.大括号里面可以引入js变量 或者 表达式       {name || ''} 3.可以做判 ...

  4. 个性化排序算法实践(一)——FM算法

    因子分解机(Factorization Machine,简称FM)算法用于解决大规模稀疏数据下的特征组合问题.FM可以看做带特征交叉的LR. 理论部分可参考FM系列,通过将FM的二次项化简,其复杂度可 ...

  5. Linux网络编程综合运用之MiniFtp实现(五)

    转眼兴奋的五一小长假就要到来了,在放假前夕还是需要保持一颗淡定的心,上次中已经对miniFTP有基础框架进行了搭建,这次继续进行往上加代码,这次主要还是将经历投射到handle_child()服务进程 ...

  6. linux网络编程之system v信号量(二)

    今天迎来元旦假期的最后一天了,过得好快~昨天跟小伙伴们在军都滑雪陪儿爽,虽说上了两回中级道都摔得异常的惨烈,但是在初级道上学习"s"转弯还是有一些小心得,可以在要往高手迈进的前提, ...

  7. P1080 【NOIP 2012】 国王游戏[贪心+高精度]

    题目来源:洛谷 题目描述 恰逢 H国国庆,国王邀请n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王 ...

  8. dt7.0百度熊掌当天主动推送方法

    因自己没事新做了一个网站,申请了一个熊掌号,所以做了这个主动推送接口,希望能收录快些,在此分享下关于DT7.0主动当天推送功能 上代码: <?php /* 百度当天主动推送熊掌功能 作者:68喜 ...

  9. 修改 hosts 完成域名绑定

    修改 hosts 完成域名绑定 mac 用户直接执行 vim /private/etc/hosts在 hosts 文件最后添加一行: 127.0.0.1a.com 这一句是什么意思呢? 就是告诉我们的 ...

  10. scrapy vs requests+beautifulsoup

    两种爬虫模式比较: 1.requests和beautifulsoup都是库,scrapy是框架. 2.scrapy框架中可以加入requests和beautifulsoup. 3.scrapy基于tw ...