在 Flask 应用中使用 gevent
在 Flask 应用中使用 gevent
普通的 flask 应用
通常在用 python 开发 Flask web 应用时,使用 Flask 自带的调试模式能够给开发带来极大便利。Flask 自带的调试模式可以让我们在程序改动时自动重新加载我们的应用程序,而且 jinja2 的模板也会随着改动自动刷新。一般用法是:
# app.py
from flask import Flask
app = Flask( __name__ )
@app.route( '/')
def hello():
return 'Hello World'
if __name__ == '__main__':
app.run( debug = True )
运行上面这个例子,就可以在本地的 5000 端口运行由 flask 提供的服务器程序。如果我们对这个文件进行修改,那么 flask 的底层框架 werkzuge 检测到文件变动后就会自动重新加载我们的应用程序。
然而 Flask 是单线程运行,如果在某个页面中执行了一些耗时的工作,那么程序就会在这里等待,无法响应其他的请求。也就是说,如果一个路由响应函数中有阻塞代码,那么其他用户无法访问这个 web 服务器,而且自己也打不开其他页面了。
在一个路由中添加阻塞代码,如下所示:
# app.py
from time import sleep
@app.route('/testsleep')
def test_sleep():
sleep( 10 )
return 'Hi, You wait for about 10 seconds, right?'
当打开 /testsleep
页面时,会发现浏览器一直在加载过程中,再去打开 /
页面,发现这个页面也是在加载中。只有等到 /testsleep
页面加载完了,才会去加载 /
页面。
在 flask 中使用 gevent
为了解决一个页面耗时导致所有页面都无法访问的问题。考虑使用 gevent 非阻塞的运行服务器程序。在引入 gevent 前,可以在程序最开始执行的位置引入猴子补丁 gevent.monkey
,这能修改 python 默认的 IO 行为,让标准库变成 协作式(cooperative)的 API。注意引入 gevent 后,不能再用原来的方式启动我们的 web 应用了:
# app.py
from gevent import monkey
monkey.patch_all() # 打上猴子补丁
from flask import flask
...
if __name__ == '__main__':
from gevent import pywsgi
app.debug = True
server = pywsgi.WSGIServer( ('127.0.0.1', 5000 ), app )
server.serve_forever()
这个时候再去打开 /testsleep
页面,还是要等待一些时间才会加载完页面,但是这个时候已经访问 /
页面将会立即加载完毕。
启用调试模式和自动刷新模板
如果在某个页面中的代码有问题,会出现运行时错误,那么访问这个页面只能看到 Internal Server Error
的提示,没有了之前的调试窗口和错误信息。而且在上面的代码中,我已经将 app
的 debug 标志设为了真,然而并没有什么用。为了启用调试模式,方便在开发时看到错误信息,我们需要用到 werkzuge
提供的 DebuggedApplication
。
# app.py
if __name__ == '__main__':
from werkzeug.debug import DebuggedApplication
dapp = DebuggedApplication( app, evalex= True)
server = pywsgi.WSGIServer( ( '127.0.0.1', 5000 ), dapp )
server.serve_forever()
重新打开首页,可以看到熟悉的错误信息。
如果你使用了模板,那么你可能已经注意到了,使用 gevent 后修改模板再次访问可能也不会看到页面上有相应的改动。那么你需要在修改 app 的配置,以便模板能够自动刷新,以下两种方式是等效的:
app.jinja_env.auto_reload = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
尝试自动重新加载
使用了 gevent 后,原有的一些功能都需要通过一定的配置之后才可以正常访问。但是有一个功能我们仍然没有解决,那就是修改代码后 web 应用不会自动重新加载了。stackoverflow 和 gist 提到的一种解决方法是使用 werkzeug 提供的 run_with_reloader,可以写出这样的代码:
# app.py
...
if __name__ == '__main__':
...
from werkzeug.serving import run_with_reloader
server = pywsgi.WSGIServer( ( '127.0.0.1', 5000 ), dapp )
run_with_reloader( server ).serve_forever()
然而如果你这样做了就会发现一点用都没有,甚至连 web 应用都不能正常启动了。
按照这个思路来的还有这段代码提供的 示例,但这个示例是将 run_with_reloader
作为装饰器来使用,以下是该示例的代码:
import gevent.wsgi
import werkzeug.serving
@werkzeug.serving.run_with_reloader
def runServer():
app.debug = True
ws = gevent.wsgi.WSGIServer(('', 5000), app)
ws.serve_forever()
然而这也没有什么作用。看一下 flask 的源代码可以发现,run_with_reloader 已经不是装饰器了。而且开发者提醒我们不要使用下面的这个函数,这个 api 很明显已经被废弃了,flask 源代码如下:
def run_with_reloader(*args, **kwargs):
# People keep using undocumented APIs. Do not use this function
# please, we do not guarantee that it continues working.
from werkzeug._reloader import run_with_reloader
return run_with_reloader(*args, **kwargs)
如果使用 gevent 作为 WSGI 的网关服务器,似乎就没法使用自动加载应用的功能了。
实现自动重新加载
没有其他可以借鉴的方法了,好在之前在查看廖雪峰的 Python 教程时,给出了一个自动重新加载应用的示例,主要原理是利用 watchdog 提供的文件监听功能,在创建、修改文件时会触发相应的处理器,这样就可以实现自动重新加载功能。代码可以去廖雪峰的教程中查看。
之后的应用启动时就不能直接使用 python app.py
了。如果将自动加载的代码保存在同级的 monitor.py
文件中,我们需要使用 python monitor.py app.py
启动应用。最终就可以自动热加载我们的 web 应用了。
关于文件改动事件,之前我也写过一个类似的 JS 程序,原理类似,都是当文件改动时自动执行重新构建应用的命令。
相应的说明代码在 github 上可以查看。
References
在 Flask 应用中使用 gevent的更多相关文章
- Flask 框架中 上下文基础理念,包括cookie,session存储方法,requset属性,current_app模块和g模块
Flask中上下文,分为请求上下文和应用上下文.既状态留存 ,就是把变量存在某一个地方可以调用 请求上下文:实际就是request和session用法理念,既都是可以存储东西. 应用上下文:既变量共享 ...
- Flask项目中整合各组件
一.介绍 主要介绍flask_sqlalchemy.flask_script.flask_migrate这三个组件该如何整合到flask项目中,以及如何使用. # 安装组件 pip3 install ...
- Flask项目中使用mysql数据库启动项目是发出警告
Flask项目中使用mysql数据库启动项目是发出警告: Warning: (1366, "Incorrect string value: '\xD6\xD0\xB9\xFA\xB1\xEA ...
- flask SQLAlchemy中一对多的关系实现
SQLAlchemy是Python中比较优秀的orm框架,在SQLAlchemy中定义了多种数据库表的对应关系, 其中一对多是一种比较常见的关系.利用flask sqlalchemy实现一对多的关系如 ...
- uwsgi+flask环境中安装matplotlib
uwsgi+flask的python有自身的virtual environment,可以通过如下命令进入 . venv/bin/activate 虽然通过sudo apt-get install py ...
- flask页面中Head标签内容为空问题
在使用flask时遇到点问题,以前还没有注意到. 生成页面的时候使用的是模板继承方式,当添加meta标题的时候,本来是添加的base.html模板中的head标签中,但是生成页面后,head中的内容却 ...
- werkzeug(flask)中的local,localstack,localproxy探究
1.关于local python中有threading local处理方式,在多线程环境中将变量按照线程id区分 由于协程在Python web中广泛使用,所以threading local不再满足需 ...
- python flask route中装饰器的使用
问题:route中的装饰器为什么感觉和平时使用的不太一样,装饰器带参数和不太参数有什么区别?被修饰的函数带参数和不带参数有什么区别? 测试1:装饰器不带参数,被修饰的函数也不带参数. def log( ...
- nginx的rewrite ,如何在flask项目中获取重写前的url
1. 在flask配一个重写到哪的路由,假设是/rewite/,然后到nginx的配置文件写重写规则,我这里重写全部的请求,接着测试能否重写成功 1. 添加一个路由 配置重写规则 测试成功 2.接下来 ...
随机推荐
- 细说Mammut大数据系统测试环境Docker迁移之路
欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言 最近几个月花了比较多精力在项目的测试环境Docker迁移上,从最初的docker"门外汉"到现在组里的同学(大部分测试及少 ...
- C# 接口(3)
这么半天说了如何使用,实现接口.相信也都发现了接口和抽象类很多相似的地方. 但是! 这两个根本就是不一样的. 抽象类 : ...
- [Algorithm]图
一.图的算法 邻接矩阵表示的数据结构 1 #define INFINITY INT_MAX // 无穷大 2 #define MAX_VERTEX_NUM 20 // 限制顶点最大数值为20个 3 # ...
- java web高级编程 笔记1
chapter1:了解web应用程序 web应用程序主要组件: Servlet 过滤器 监听器 JSP chapter2:各类web容器介绍 略 chapter3:Servlet介绍 Servlet是 ...
- 解决低版本Xcode不支持高版本iOS真机调试的问题
1.现象截图 Could not locate device support files. This iPhone 6s is running iOS 11.1 (15B93), which may ...
- 可以避免的10大IT面试误区
在所有的面试过程当中,很多面试者都普遍犯了这样一个错误——由于对面试准备的不足,所以对于应聘公司的背景,以及一些最基本的信息都缺乏了解,有些面试者甚至对要面试的公司毫无所知.这些数据并不是空穴来风,A ...
- 通过ssh访问NAT网络模式下的虚拟机Linux
链接:https://blog.csdn.net/jiuduan2009/article/details/51737004 https://blog.csdn.net/disalone201107/a ...
- 消息中间件ActiveMQ、RabbitMQ、RocketMQ、ZeroMQ、Kafka如何选型?
最近要为公司的消息队列中间件进行选型,市面上相关的开源技术又非常多,如ActiveMQ.RabbitMQ.ZeroMQ.Kafka,还有阿里巴巴的RocketMQ等. 这么多技术,如何进行选型呢? 首 ...
- 更改Linux下的时间
1.使用tzseletect glibc-common-2.12-1.192.el6.x86_64 : Common binaries and locale data for glibc Repo : ...
- JDBC_事务概念_ACID特点_隔离级别_提交commit_回滚rollback
事务的概念 一组要么同时执行成功,要么同时执行失败的SQL语句,是数据库操作的一个执行单元! 事务开始于: 连接到数据库上,并执行一条DML语句(insert,update或delete),前一个事务 ...