请教一下 Flask + Requests + 多线程 问题

2019-05-20 21:42:12 +08:00
 yuting0501
最近在做一个 Demo:
[硬件 GPS 模块] 采集 GPS 数据发送到 [物联网服务器] , [我的网站] 定时 10 秒向 [物联网服务器] 请求 GPS 数据更新信息。

现在遇到 requests 请求 gps 数据时候阻塞,导致网站卡顿,故想到使用 Python 的多线程解决:
线程 A: 主线程,跑 flask
线程 B:Requests 线程,睡眠 10 秒,唤醒请求 GPS 数据

不考虑使用前端请求,就讨论用 Python 线程解决,如果是嵌入式的话可以这样做:
1. 在线程 A 中创建低优先级的线程 B
1. 有网络请求时执行线程 A,空闲时执行线程 B,这样既不影响用户网页请求,又可以发起 Requests 请求更新 GPS 数据

回到 Flask 和 Python,查了一下 Flask 也有多线程模式,我尝试开启 threaded 选项,并未解决阻塞问题。

查了 Python 3.7 的新特性,asyncio,能否使用 asyncio 解决多线程问题呢?

对 Python 的理解很粗浅,顺便上两个没太理解的链接:
https://hackernoon.com/how-to-run-asynchronous-web-requests-in-parallel-with-python-3-5-without-aiohttp-264dc0f8546

https://medium.com/velotio-perspectives/an-introduction-to-asynchronous-programming-in-python-af0189a88bbb
3689 次点击
所在节点    Python
15 条回复
coolair
2019-05-20 22:36:14 +08:00
用 celery 啊
im67
2019-05-20 22:51:14 +08:00
会不会是 flask 的多线程模式跟你要的多线程模式有区别?
1iuh
2019-05-20 23:22:07 +08:00
gevent
Qzier
2019-05-20 23:37:14 +08:00
几种解决方法:
1. 多线程,不是让你开启 flask 的多线程模式,而是用 threading 模块再开启一个线程跑你的任务
2. flask 换成 starlette,requests 换成 requests-async,全部写异步代码
3. celery
Qzier
2019-05-20 23:39:17 +08:00
4. 多进程,不是 CPU-bound 任务的话没必要,多线程就可以解决了
rogwan
2019-05-20 23:43:23 +08:00
你这个阻塞是网络问题,不是机器性能问题。就算用 thread 消耗也不大,先优化下网络请求,不行再优化性能方面
429839446
2019-05-21 00:06:59 +08:00
不如 mqtt
vZexc0m
2019-05-21 11:51:19 +08:00
你解决的问题的思路没对。
1. 更新 GPS 数据应该采用异步定时任务实现,可用 celery 或者 dramatiq+apscheduler 实现。
2. flask 部署采用 gunicorn+gevent 实现就行了。
yuting0501
2019-05-21 12:39:52 +08:00
谢谢大家!

@vZexc0m @Qzier @coolair

试了一下 celery,有点云里雾里的,只是想用多线程解决 Requests 阻塞问题,为什么 celery 要用到 redis,我再了解看看。
1iuh
2019-05-21 22:00:58 +08:00
楼主呀,gevent 就是最好的解决方案。 不用上 celery。。
CEBBCAT
2019-05-22 01:05:35 +08:00
读起来真有点头疼,我翻来覆去看了三四遍也没看太懂。

『现在遇到 requests 请求 gps 数据时候阻塞,导致网站卡顿,故想到使用 Python 的多线程解决:』

1. requests 请求数据为什么会有阻塞?调物联网服务器接口不是调个 API 吗?几百 ms 吧?
2. 网站为什么有卡顿?用户每次看网页都要从物联网服务器取数据呀?为什么不把数据准备好呢?
3. 架构不明,不知道为什么使用多线程

您可以使用 ProcessOn 绘制图片
yuting0501
2019-05-22 09:51:37 +08:00
@1iuh 感谢两次推荐,我去了解一下 gevent 用法。
yuting0501
2019-05-22 10:16:23 +08:00
@CEBBCAT 抱歉,背景可能没有交代清楚

> 1. requests 请求数据为什么会有阻塞?调物联网服务器接口不是调个 API 吗?几百 ms 吧?
这只是个 Demo,我的 Flask 网站调用物联网服务器接口,物联网服务器向 IOT 设备请求数据,IOT 设备返回给物联网服务器,物联网服务器再返回我的 Flask 网站,中间可能耗时 3、4 秒。简单来说姑且认为是一个 Requests 请求会耗时 3、4 秒。实际上即便几百 ms 也是不可接受的,requests 是阻塞请求,在此期间用户向我的网站请求网页无法得到及时响应。

> 2. 网站为什么有卡顿?用户每次看网页都要从物联网服务器取数据呀?为什么不把数据准备好呢?
可以准备好,例如在最新数据更新前先返回历史数据。但总会遇到 Requests 阻塞场景,想知道大家怎么解决。

> 3. 架构不明,不知道为什么使用多线程
简单来说,就做两件事情,
1. 运行 Flask,响应网页请求
2. 间隔 10 秒像第三方物联网服务器发起 Requests 请求

矛盾冲突:
由于 Requests 请求耗时较长,导致 Flask 程序无法及时响应网页请求。

> 为什么使用多线程?
可能用了错误的思维去解决这个问题,因为在嵌入式中,同优先级两个任务会共享 CPU 时间,时间片轮转,这样就能并行处理上述的两件事情。
CEBBCAT
2019-05-22 13:06:23 +08:00
@yuting0501 #13

1. 为什么有阻塞
噢!我现在明白多了。我想这是架构的问题——不知道架构这个取词是否洽当——,要是我来开发不会让数据每次都要这么走一遍的,各个节点都会有缓存 /数据库。要是换成 NBIOT 那种网络,你可能就不会让数据这么走了,嘻嘻。

2. 可是使用缓存,但想问问大家怎么最优地解决 Requests 阻塞
『总会遇到 Requests 阻塞场景』,在 Web 服务器(也就是 Flask 吧?)每秒更新一次数据,用的时候直接用 Web 服务器上积累下来的数据,这和普通的页面速度就一样了吧?

我的意思可能是……解耦?

3. 现在是什么架构?
我想前两问可以体现我的思想了,再多的俺也不会了
yuting0501
2019-06-01 08:25:58 +08:00
@1iuh 再次感谢,gevent 是正确的解决方案。

总结一下这个问题,用 threading 是不对的,Python 中的 threading 没有优先级区分,也就是说执行到 B 线程的 requests 耗时请求,CPU 还是会死等。同时 Python 的 threading 不能被销毁、停止、暂停、恢复或中断。

什么情况下用 threading?
当你每个 thread 中的任务的每行代码都是在干实事,没有等待、睡眠等无意义操作时可以用 threading。

目前还在看资料,想搞懂有了 async 后是否还有使用 celery 和 gevent 的必要以及它们的区别。

参考资料:
https://docs.python.org/zh-cn/3.6/library/threading.html

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

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

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

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

© 2021 V2EX