如何快速把 Python 同步代码中的网络请求部分替换成异步代码

2019-10-24 10:58:22 +08:00
 itskingname

我有一个 ETL 程序,将会按顺序实现如下功能:

  1. 从数据源读取数据
  2. 对数据进行脱敏
  3. 对数据进行清洗
  4. 分析每一条数据的情感
  5. 数据存入中间表

其中,第 4 步是使用 requests 来发送 post 请求到一个 NLP 分析接口实现的。现在由于数据量增加,第 4 步的网络请求成为了瓶颈,因此希望把第 4 步改成 aiohttp.

但是,由于 aiohttp 需要使用 async/await, 如果从 0 开始的话,要加入事件循环,每个相关的函数也要改为 async def 来定义,而且也难以使用 class。

单独从 0 开始使用 async/await 重写这个 ETL 程序当然没有问题,但是有没有办法以最小的代码修改量替换第 4 步操作?最好能实现 1235 步的代码都不需要改动。

1045 次点击
所在节点    问与答
7 条回复
shoaly
2019-10-24 11:08:30 +08:00
换个思路, 第四步用队列去完成
ClericPy
2019-10-24 11:17:20 +08:00
以前学习的时候实现过同步代码里使用协程

from torequests.dummy import Requests
req = Requests(frequencies={'p.3.cn': (2, 2)})
tasks = [
req.get(
'http://p.3.cn',
retry=1,
timeout=5,
callback=lambda x: (len(x.content), print(req.frequencies, flush=1)))
for i in range(4)
]
req.x # or [task.x for task in tasks]
result = [task.cx for task in tasks]
print(result)


这里的 cx 是 callback 的返回结果, 如果没有 callback, 直接使用 task.x 就有了, 和普通协程比, 性能损失倒也不算大. 不过还是习惯在全局协程里用
locoz
2019-10-24 11:21:39 +08:00
加入中间件,将第四部拆出来放到另一个程序中处理
Trim21
2019-10-24 11:27:09 +08:00
只在第四步里开一个事件循环,并发请求

可以考虑一下 httpx,跟 requests 是 api 兼容的,
Trim21
2019-10-24 11:30:37 +08:00
@Trim21 虽然还没完全稳定下来,不过自带一个并行请求的功能(底层还是 asyncio )

https://www.encode.io/httpx/parallel/
itskingname
2019-10-24 11:36:45 +08:00
@Trim21 我看一看,感谢
itskingname
2019-10-24 12:34:26 +08:00
@locoz 最后没有更好的办法时,确实会使用这个办法。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/612418

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX