写在前面:编程
asyncio 初学者可能较难理解,能够结合我前面的几篇文章一块儿食用:微信
原视频为 PyCon2019上的一场技术分享,做者是 Spotify的工程师, 经过一个案例, 介绍了 asyncio 的一些 best practice。并发
并发地 publish message:注意图中高亮的那一段,这里用的不是await queue.put(msg)
,这是由于await
会阻塞while
循环(参考前面的一篇文章:异步编程 101:asyncio中的 for 循环),也就是说,要等queue.put(msg)
完成了,下一趟才会开始。而asyncio.create_task()
会立刻 "fire" 而且当即返回,你能够理解为fire and forget machinism。异步
若是你没时间看异步编程 101:asyncio中的 for 循环,我简要回顾一下:
await
作的事情是把当前的协程挂起,把控制权交给事件循环,以便于事件循环有其余协程能够调度时,接着运行其余协程。可是对于执行await
的这个协程而言,它是被阻塞的。这个例子中,publish()
中的while
循环是一个总体。async
这里使用msg = await queue.get()
是make sense 的,由于你得先获得 message 而后才能接着作其余事情。然后面的restart_host
则用create_task
,由于咱们不想对他await
(等待它完成)而阻塞了整个 while Ture
循环。ide
收到 message 以后,除了restart_host()
以外,咱们可能还须要作一些其余的任务,好比持久化保存message
。异步编程
这只须要在consume
方法里面再添加一个create_task()
:函数
然而有时候咱们是但愿异步任务可以serial
执行的。若是要把restart_host()
的逻辑改一下:先获取上次重启时间,而后判断上次重启时间是否是大于7天,若是是,再 restart_host()
。这里的last_restart_date()
和restart_host()
是有明确前后顺序的。post
可是咱们又不想这里的线性执行影响后面的 message 获取,很简单,只要把这个逻辑封装成一个协程,而后create_task()
就行。性能
须要对message
ack
,这样producer
才不会从新发送。因此如今处理消息的逻辑以下:
须要保证:save
和restart_host
所有完成以前,才能cleanup
。
使用await
是可以 work 的,可是性能确定不够。
因此asyncio.gather()
就派上用场了:这里把save()
和restart_host()
两个协程交给gather
,并传给它一个callback
,等两个任务所有完成以后调用callback
函数,也就是cleanup()
。
若是不想用 callback
,也能够直接await
gather
,这样的 code 更加 clean:
最后把程序跑一下,图中不一样颜色表示的是同一个 message:
save
和restart_host
所有结束以后,才ack
message。把publish
和consume
组合起来,获得最后的main()
。
不是说你在原来代码的基础上加上async
、await
就能得到并发性能的,极可能你的异步代码仍是 sequantial 的。
一些有前后顺序的操做,不意味着就必定是要 block 的。好比先作A 在作 B,在等待 A 完成的过程当中,我能够抽出时间作 C。放到本文的例子来讲,我须要先save
和restart_host
才能cleanup
,但我能够在等待save
和restart_host
的时候继续作其余事情:把控制权交给主事件循环,接着运行其余协程。
await
的做用是什么?asyncio.create_task()
asyncio.gather()
若是你像我同样真正热爱计算机科学,喜欢研究底层逻辑,欢迎关注个人微信公众号: