首页  >  问答  >  正文

python - Flask 阻塞的问题

伊谢尔伦伊谢尔伦2763 天前617

全部回复(6)我来回复

  • 高洛峰

    高洛峰2017-04-17 17:19:29

    Flask 自带的服务器显然不能用在生产环境,我个人建议跑在 gunicorn 或者 uwsgi 上,外层还应该有 nginx,通过一些精心设计的参数(没有万能公式,需要在实践基础上调整)能够在服务器资源占用并发能力上取得较好的平衡。总之就是绝对不能单进程也不使用任何并发技术就放在生产环境,那绝对不耐操啊!

    我之前的经验使用 gevent 效果并不是那么理想(那时候还没有 1.0)并且 monkey patch 会导致一些额外的问题。所以不是特别推荐。

    回到 OAuth 这个问题上,既然提到问题是出在获取 access_token 这一步,那么就是说采用的是 OAuth 的 Authorization Code Grant 流程,并且进入了最后一步。我们回顾一下这个流程(摘自 OAuth 2 规范):

         +----------+
         | Resource |
         |   Owner  |
         |          |
         +----------+
              ^
              |
             (B)
         +----|-----+          Client Identifier      +---------------+
         |         -+----(A)-- & Redirection URI ---->|               |
         |  User-   |                                 | Authorization |
         |  Agent  -+----(B)-- User authenticates --->|     Server    |
         |          |                                 |               |
         |         -+----(C)-- Authorization Code ---<|               |
         +-|----|---+                                 +---------------+
           |    |                                         ^      v
          (A)  (C)                                        |      |
           |    |                                         |      |
           ^    v                                         |      |
         +---------+                                      |      |
         |         |>---(D)-- Authorization Code ---------'      |
         |  Client |          & Redirection URI                  |
         |         |                                             |
         |         |<---(E)----- Access Token -------------------'
         +---------+       (w/ Optional Refresh Token)
    
       Note: The lines illustrating steps (A), (B), and (C) are broken into
       two parts as they pass through the user-agent.

    理论上,浏览器通过步骤 (C) 将请求发给 Flask 之后,就没浏览器什么事了(后续的事情不在流程里了),Flask 可以简单给一个 200 OK,并设计让浏览器通过 AJAX 稍后再从 Flask 请求数据,不必再苦苦等待 Flask 去执行步骤 (D) 并等待 (E) 了((E) 就是出现问题的步骤)。

    Flask 当然可以直接去请求,如果做好了前面的并发处理和负载均衡,且用户数不是非常多的话。当然,如果请求规模足够大,Flask 可以发起一个异步任务,让专门的任务执行部件去执行操作。

    之前,我开发的一个项目使用了 celery 通过 RabbitMQ 发送任务,由专门的程序处理这些需要一定时间才能完成的工作,任务结果存储在 Redis 中,每天处理数千万次类似的接口调用。

    具体的,当 (C) 的请求到达 Flask 之后,Flask 向 RabbitMQ 发一个任务,就返回 200 OK 了。相应的任务执行部件执行步骤 (D) 并得到响应 (E),结果写入 Redis。浏览器过一段时间再次请求 Flask 要后续的信息,这时候 Flask 去 Redis 查询任务执行成功了没有,并做出对应的响应。

    希望对题主有所帮助。

    回复
    0
  • 大家讲道理

    大家讲道理2017-04-17 17:19:29

    你可以使用gevent将 Flask 异步化。

    在程序最开始处加入:

    from gevent import monkey
    monkey.patch_all()

    参考:

    • http://sergray.me/asynchony-in-the-flask.html

    • http://xlambda.com/gevent-tutorial/#monkey-patching

    回复
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-17 17:19:29

    flask本来就是单线程的,可以通过它的wsgi,在前面加个nginx或者gunicorn之类的服务器实现多线程。tornado是单线程异步的,好像无法与flask兼容吧。

    回复
    0
  • 巴扎黑

    巴扎黑2017-04-17 17:19:29

    我来看看大家如何解决这个问题。囧。。。

    ps:题主既然使用了DEBUG模式,可以把日志贴出来。

    我在视图中用urllib2试了一下访问谷歌,还是有反应的,只不过最终会报错:

    在测试的时候,Flask响应的时间的确很慢。处理这种异步任务,建议使用Celery,目前也在学习使用Celery

    回复
    0
  • PHP中文网

    PHP中文网2017-04-17 17:19:29

    自答一下,Flask可以开启多线程,不过生产环境还是要用Nginx才行:

    if __name__ == '__main__':
        app.run(host='*.*.*.*', port=80,threaded=True)

    回复
    0
  • 阿神

    阿神2017-04-17 17:19:29

    Flask自带的serverwerkzeug,默认的werkzeug是单线程,对外请求就block了。可以入 @柳易寒 那样开启多线程即可。

    Flask本身不是单进程单线程的,生产环境下,通过uwsgi或者gunicorn实现多线程方式。flask的用法没错,flask部署方式得结合uwsgigunicron甚至是gevent

    如果不用关心异步请求的结果,可以使用 Redis实现简单消息队列或celery实现异步请求

    回复
    0
  • 取消回复