请教一下 Flask + Requests + 多线程 问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
yuting0501
V2EX    Python

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

  •  
  •   yuting0501 2019-05-20 21:42:12 +08:00 4373 次点击
    这是一个创建于 2339 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近在做一个 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
    15 条回复    2019-06-01 08:25:58 +08:00
    coolair
        1
    coolair  
       2019-05-20 22:36:14 +08:00 via Android   1
    用 celery 啊
    im67
        2
    im67  
       2019-05-20 22:51:14 +08:00   1
    会不会是 flask 的多线程模式跟你要的多线程模式有区别?
    1iuh
        3
    1iuh  
       2019-05-20 23:22:07 +08:00   1
    gevent
    Qzier
        4
    Qzier  
       2019-05-20 23:37:14 +08:00   1
    几种解决方法:
    1. 多线程,不是让你开启 flask 的多线程模式,而是用 threading 模块再开启一个线程跑你的任务
    2. flask 换成 starlette,requests 换成 requests-async,全部写异步代码
    3. celery
    Qzier
        5
    Qzier  
       2019-05-20 23:39:17 +08:00
    4. 多进程,不是 CPU-bound 任务的话没必要,多线程就可以解决了
    rogwan
        6
    rogwan  
       2019-05-20 23:43:23 +08:00 via Android   1
    你这个阻塞是网络问题,不是机器性能问题。就算用 thread 消耗也不大,先优化下网络请求,不行再优化性能方面
    429839446
        7
    429839446  
       2019-05-21 00:06:59 +08:00   1
    不如 mqtt
    vZexc0m
        8
    vZexc0m  
       2019-05-21 11:51:19 +08:00   1
    你解决的问题的思路没对。
    1. 更新 GPS 数据应该采用异步定时任务实现,可用 celery 或者 dramatiq+apscheduler 实现。
    2. flask 部署采用 gunicorn+gevent 实现就行了。
    yuting0501
        9
    yuting0501  
    OP
       2019-05-21 12:39:52 +08:00
    谢谢大家!

    @vZexc0m @Qzier @coolair

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

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

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

    您可以使用 ProcessOn 绘制图片
    yuting0501
        12
    yuting0501  
    OP
       2019-05-22 09:51:37 +08:00
    @1iuh 感谢两次推荐,我去了解一下 gevent 用法。
    yuting0501
        13
    yuting0501  
    OP
       2019-05-22 10:16:23 +08:00   1
    @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
        14
    CEBBCAT  
       2019-05-22 13:06:23 +08:00
    @yuting0501 #13

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

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

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

    3. 现在是什么架构?
    我想前两问可以体现我的思想了,再多的俺也不会了
    yuting0501
        15
    yuting0501  
    OP
       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
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     916 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 20:48 PVG 04:48 LAX 13:48 JFK 16:48
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86