最近了解了下 Python3 的 async/await 用起来跟 NodeJS 的差不多,找到的异步 Redis 和 Mongodb 库都还不错。
发现 requests 没有异步,想找个替代的,aiohttp 的语法太奇怪了,如下,得先创建一个 session,然后 xxx,写起来很是麻烦,特别是要把以前的同步代码改为异步的,突然想要放弃。
import aiohttp
import asyncio
async def main():
async with aiohttp.ClientSession() as session:
pokemon_url = 'https://pokeapi.co/api/v2/pokemon/151'
async with session.get(pokemon_url) as resp:
pokemon = await resp.json()
print(pokemon['name'])
asyncio.run(main())
NodeJS 的 Promise 就非常爽,没有异步的自己包装下就好了,像 sleep 。
1
Vegetable 2021-08-23 17:40:57 +08:00
异步网络请求库都是这模样。
https://github.com/encode/httpx > HTTPX builds on the well-established usability of requests |
2
HongTang 2021-08-23 17:43:16 +08:00
python 不是假并发吗
|
3
kasheemlew 2021-08-23 17:49:52 +08:00
python 也可以这样:
```python import asyncio import requests async def main(): loop = asyncio.get_event_loop() futures = [ loop.run_in_executor(None, requests.get, 'http://www.baidu.com') for _ in range(100) ] await asyncio.gather(*futures) asyncio.run(main()) ``` |
4
libook 2021-08-23 18:13:24 +08:00
只要从同步代码重构为 async/await,基本都是要一层一层都改成 async/await 写法,包括 JS 在内的各个语言都是这样的。
aiohttp 的这个流程跟 Node.js 的 http module 基本是一致的,都是: 1. 创建 HTTP 客户端; 2. 创建请求; 3. 向请求流中写入数据,然后发送流结束; 4. 从返回流接收数据,直到流结束。 |
5
balabalaguguji OP @libook #4 axios 好用,python 没找到那么好用的。
|
6
LeeReamond 2021-08-23 18:15:51 +08:00 2
1 、requests 的正常用法也包括创建 session,不用只是因为写的 demo 需求场景太简单了而已。
2 、因为 Python 存在同步宇宙与异步宇宙两种东西,你要在同步宇宙里创造异步宇宙 j 就需要手搓一个事件循环,所以采用了这种写法。node 的事件循环是与生俱来送给你的,所以你在 node 中不需要额外写法,但同样地这令进入同步宇宙变得困难。 3 、Python 也可以非常简单地将同步逻辑封装为协程。 |
7
aladdinding 2021-08-23 18:16:02 +08:00
使用了 session 应该是会和浏览器一样进行 tcp 的连接复用 跟浏览器一样
|
8
aladdinding 2021-08-23 18:16:29 +08:00
requests 也有 session
|
9
janxin 2021-08-23 18:17:24 +08:00
最大问题是侵入性的,至少要做额外适配
客户端推荐 httpx |
10
keepeye 2021-08-23 18:36:51 +08:00
自己可以封装一下,例如
async def post(url, data: bytes, proxy=None, **kwargs): async with aiohttp.ClientSession().post(url, data=data, proxy=proxy, **kwargs) as response: return response |
11
keepeye 2021-08-23 18:37:26 +08:00
缩进怎么没了 ,谁知道评论怎么发代码?
``` async def post(url, data: bytes, proxy=None, **kwargs): async with aiohttp.ClientSession().post(url, data=data, proxy=proxy, **kwargs) as response: return response ``` |
12
balabalaguguji OP @keepeye #10 好嘞,感谢
|
13
ClericPy 2021-08-23 20:39:44 +08:00
我是自己缓存一个 Session 对象然后到处引用...
比如这句 async with aiohttp.ClientSession() as session 拆成 self.session = aiohttp.ClientSession() 然后 await self.session.__aenter__() 到 结束时候 await self.session.__aexit__(None, None None) 就行了 用起来还凑合吧, 至于 httpx, 性能比 aiohttp 差一倍多, 不得不选了后者, 然后自己加了 wrapper 把 aiohttp 打包成 requests 那个用法... |
14
ysc3839 2021-08-23 20:42:53 +08:00 via Android 1
@LeeReamond “需要手搓一个事件循环”这是 Python 加的限制。理论上无栈协程是可以直接替代回调函数使用的,不需要什么事件循环。比如 C++的协程在 await 一个对象的时候,被 await 的对象能拿到这个协程的回调函数,执行这个回调函数就能恢复协程执行。
js 也与此类似,它的异步跟事件循环关系并不大。 但是 Python 的不一样,它一定要一个事件循环或者说调度器才能跑起来,不能像 C++那样让被等待方控制恢复执行。 |
15
ericls 2021-08-23 21:06:07 +08:00
测过 uvloop 性能和 nodejs 一模一样. 但是 CPU heavy 的东西还是慢一些
|
16
crclz 2021-08-23 21:43:39 +08:00
一点也不奇怪,每一个 async,await 都有其存在的价值。只要理解了就好了。
https://gist.github.com/crclz/2308cc72cc3f37836a6cab22c1981849 |
17
iyaozhen 2021-08-23 23:31:16 +08:00
个人感觉新业务可以用
但前提你要保证你自己确实对这个深入理解了,不然出问题没人帮你 java 那帮人不了解这个 因为 Python 天生是同步的,一旦出了问题你就得背锅了,之前有个对口的同事就这样走人了 |
19
kuangwinnie 2021-08-24 07:00:10 +08:00
@keepeye 评论不能发代码
|
20
abersheeran 2021-08-24 09:09:20 +08:00 1
跟 Nodejs 比的不足之处大抵在于 Nodejs 天然自带一个 loop,Python 需要你显式创建 loop 。而且 Nodejs 里原生都是异步的,不需要自己注意。Python 里原生都是同步的,需要自己时刻注意。
|
21
qW7bo2FbzbC0 2021-08-24 10:01:17 +08:00
@crclz #16 最新的 HTTPClient 不是自带 async 方法了吗
|
22
Nich0la5 2021-08-24 10:12:54 +08:00
python 异步几个蛋疼的点 await 传染,具体的异步实现依赖于第三方库而且场景覆盖不全,像 aiohttp aiofile 一直有海量 bug,( httpx 相对好一些维护的人比较多),自己从头撸一个异步库又要从底开始太麻烦了,我就是嫌麻烦才用 py 的。我自己写的时候经常是线程协程混写,只有明显协程性能占优的场景才用。
至于你说的 session 问题,request 也有,而且推荐这种写法,你可以自己测下性能,复用 session 和不带的差距还是很大的 |
23
wangyzj 2021-08-24 10:23:01 +08:00
天生异步和假异步
python 不是干这个的 能写而已 |
24
zzlhr 2021-08-24 10:26:25 +08:00
python 写轮询 http 都能假死。。。
|
25
enrolls 2021-08-24 11:38:28 +08:00
使用 3.9 版本,語法會變簡單。或者試試
|
26
enrolls 2021-08-24 11:38:49 +08:00
使用 3.9 版本,語法會變簡單。或者試試 curio
|
27
mmdsun 2021-08-24 13:17:27 +08:00 via Android
async/await 还是 C sharp 最舒服。
其他语言有 async/await 但没学到 C#异步的精髓。 |
28
meiyoumingzi6 2021-08-24 20:36:02 +08:00
主要还是生态吧, 感觉写起来好点, 单还是不太爽
还是 golang 写异步爽的起飞 |
29
Trim21 2021-08-24 23:18:34 +08:00
如果你要替换 requests 的话应可以用 httpx.AsyncClient,基本上就是把 requests.Session 的 http 请求换成了异步的。
如果是长时间运行的服务的话本来就不应该用 requests.get ,应该整个程序初始化一个或者多个 requests.Session,然后复用 session,跟 aiohttp 强制你要做的事情是差不多的,aiohttp 的文档里面也提到了不要每次请求都创建一个 session |
30
lewinlan 2021-08-25 02:29:48 +08:00 via Android
都 2021 年了,还写 py,放过自己好吗?
|
31
balabalaguguji OP @Trim21 #29 好的,多谢。我的连接都是一次一个的,似乎没必要保持一个 session
|
32
molika 2021-08-25 09:15:53 +08:00
习惯就好了 ~ 可能需要注意的就是各种三方库了 好多都是同步的。要自己 hack 打补丁 很痛苦。
|
34
balabalaguguji OP @molika #32 想想还是放弃了
|