gevent学习
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
函数及其升级版:poll
和epoll
。《深入理解计算机系统》一书中,对此有深入探讨。
greenlet
是一个python
的协程管理、切换模块。通过greenlet
,你可以显式地在不同的任务之间切换。
0x01 Actor模式
Actor
是Erlang
语言的精髓。它强调基于消息传递的并发机制。
我们模仿这篇文章 建立一个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()
被调用。
由于gevent
和monkey 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学习的更多相关文章
- python 协程库gevent学习--gevent数据结构及实战(四)
一不留神已经到第四部分了,这一部分继续总结数据结构和常用的gevent类,废话不多说继续. 1.Timeout错误类 晚上在调试调用第三方接口的时候,发现有些接口耗时非常多,觉得应该有个超时接口来限制 ...
- python 协程库gevent学习--gevent数据结构及实战(三)
gevent学习系列第三章,前面两章分析了大量常用几个函数的源码以及实现原理.这一章重点偏向实战了,按照官方给出的gevent学习指南,我将依次分析官方给出的7个数据结构.以及给出几个相应使用他们的例 ...
- python 协程库gevent学习--gevent源码学习(二)
在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问: 1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用. 2. 关于在使用mon ...
- python 协程库gevent学习--源码学习(一)
总算还是要来梳理一下这几天深入研究之后学习到的东西了. 这几天一直在看以前跟jd对接的项目写的那个gevent代码.为了查错,基本上深入浅出了一次gevent几个重要部件的实现和其工作的原理. 这里用 ...
- 【python】gevent学习
之前测试了stackless,感觉不太好. 不过python作为最火的脚本语言,还是吸引力难挡. python的协程方案,除了stackless,还有greenlet, 相应的事件框架也有gevent ...
- Python gevent学习笔记-2
在上一篇里面介绍了gevent的最主要的功能,先来来了解一下gevent里面一些更加高级的功能. 事件 事件是一种可以让greenlet进行异步通信的手段. ? 1 2 3 4 5 6 7 8 9 1 ...
- Python gevent学习笔记
gevent是Python的一个用于网络IO的函数库,其中应用到了 coroutine(协同程序) 的思想.首先来了解下目前网络框架的几种基本的网络I/O模型: 阻塞式单线程:这是最基本的I/O模型, ...
- 协程gevent学习
import gevent def f1(): print(11) gevent.sleep(2) print(33) def f2(): print(22) gevent.sleep(1) prin ...
- flask 实现异步非阻塞----gevent
我们都知道,flask不支持异步非阻塞的请求,我们可以创建一个新项目去测试一下,推荐大家使用pycharm去开发我们的flask 使用特别的方便. rom flask import Flask im ...
随机推荐
- 团队高效率协作开发的秘密武器-APIDOC
团队高效率协作开发的秘密武器 1.前言 在团队协作开发中,不知道各位有没有遇到这样的问题: l 新人接手了项目代码,因没有项目文档,只能靠追踪路由,寻读代码分析业务逻辑 l 前端同学写好了页面,苦等后 ...
- 使用Dell iDRAC服务器远程控制安装操作系统简要图解
使用Dell iDRAC服务器远程控制安装操作系统简要图解 iDARC tools iDRAC又称为Integrated Dell Remote Access Controller,也就是集成戴尔 ...
- SQL SERVER中如何查找存储过程中一段代码
select b.name ,a.text from syscomments a,sysobjects b where and object_id(b.name)=a.id and b.xtype i ...
- jmeter接口测试json详解
本篇围绕jmeter(压力测试工具),请求json与返回json串处理进行解析,初入测试,理解如有不对的地方请大家及时提点~~ 在这里jmeter工具的使用不在做解释 首先说下乱码问题,在这里无脑5步 ...
- Git----基础常用的命令总结
基础常规操作 git init git clone <版本库的网址> <本地目录名> touch xxx 本地创建一个xxx的文件 git add xxx git commit ...
- Java8新特性--函数式编程
在jdk8中什么是函数式接口: 1.被@FunctionalInterface注解修饰的. 2.接口里边只有一个非default的方法. 满足以上2个条件的即为函数式接口,ps:即使一个接口没有被@F ...
- List集合和Set集合UML图总结
1.List和Set,用RationalRose展示 2.Map
- spark读文件写入mysql(scala版本)
package com.zjlantone.hive import java.util.Properties import com.zjlantone.hive.SparkOperaterHive.s ...
- parted分区命令
Parted是一个比fdisk更高级的工具,它支持多种分区表格式,包括MS-DOS和GPT.它允许用户创建,删除,调整大小,缩小,移动和复制分区,重新组织磁盘使用,以及将数据复制到新硬盘,但在缩小分区 ...
- 8.1.T1
string 题面什么的 抱歉,被我咕咕咕了 考场思路: sort大法好 n2log2n过 40% 令人着实兴奋 正解: 线段树+桶 利用只有26个字母的优势 好吧,26个字母,只怪我没想到 代码: ...