距离上一篇有一段时间了,上次扯了点跟flask相关的内容,这次抽空记录下flask的机制.flask之所于上手容易,是因为我们对python语言稍微了解的话,简单几步就可以把一个小应用跑起来,不需要搭建额外的东西,所以对于并发量没有要求的话flask可以轻松应对.
flask单应用
我们在网上看到的flask教程基本都会有这样一句代码
1 | app = Flask(__name__) |
每个 Flask 应用必须创建一个Flask类的实例,并且把模块的名称传递给该实例,flask支持多应用共存,甚至是可以跟django实例共存这个后续再说,这里只是指定了一个flask实例,并不能运行起来.
Flask()更多的参数请见这里
1 | app.run(host="0.0.0.0", port=19090, debug=True, passthrough_errors=True, threaded=True) |
有了上面这句话之后,flask实例才真正的运行起来,有几个重要的参数这里要说下:
- host=0.0.0.0:表示其它主机都能访问
- debug=True:开启调度模式,一般用于测试环境
- passthrough_errors=True: 禁用错误捕获
- threaded=True: 则是开启多线程模式,让应用能够同时处理多个请求
更多的参数请见这里
最基本的例子:
1 | from flask import Flask,render_template |
现在,打开浏览器输入http://ip:19090/index.html
即可访问index页面
面对多个请求时 ,flask是如何做到请求隔离的呢,这里要提一个flask的两个上下文机制
两个上下文
这里不深入源代码一探究竟,一句话:临时全局变量,flask很机智的实现了thread.local类似线程隔离
功能,并配置栈
这种数据结构让其很轻松的可以将对象推入、弹出、快速获取栈顶对象,当然这些操作也是线程隔离的.
也就是说,在一次请求的一个线程中可以将其设置为全局变量,但是仅限于请求的这个线程内部,不同线程通过“线程标识符”来区别,这样就不会影响到其他线程的请求.
请求上下文
实现线程隔离后,为了在一个线程中更加方便使用这些变量,flask中还有一种堆栈的数据结构(通过werkzeug的LocalStack实现),可以处理这些变量,但是并不直接处理这些变量.假如有一个程序得到一个请求,那么flask会将这个请求的所有相关信息进行打包,打包形成的东西就是处理请求的一个环境.flask将这种环境称为请求上下文
(request context),之后flask会将这个请求上下文对象放到堆栈中.
这样,请求发生时,我们一般都会指向堆栈中的“请求上下文”对象,这样可以通过请求上下文获取相关对象并直接访问,例如request、session、current_app,g
(后两者为应用上下文).还可以通过调用对象的方法或者属性获取其他信息,例如request.method,等请求结束后,请求上下文会被销毁,堆栈重新等待新的请求上下文对象被放入.
应用上下文
应用上下文的概念是在flask 0.9中增加的
当在一个应用的请求上下文环境中,需要嵌套处理另一个应用的相关操作时(这种情况更多的是用于测试或者在console中对多个应用进行相关处理),“请求上下文”显然就不能很好地解决问题了,因为魔法current_app无法确定当前处理的到底是哪个应用.如何让请求找到“正确”的应用呢?我们可能会想到,可以再增加一个请求上下文环境,并将其推入栈中.由于两个上下文环境的运行是独立的,不会相互干扰,所以通过调用栈顶对象的app属性或者调用current_app(current_app一直指向栈顶的对象)也可以获得当前上下文环境正在处理哪个应用.这种办法在一定程度上可行,但是如果说对第二个应用的处理不涉及到相关请求,那也就无从谈起“请求上下文”,更不可能建立请求上下文环境了.为了应对这个问题,Flask中将应用相关的信息单独拿出来,形成一个“应用上下文”对象.这个对象可以和“请求上下文”一起使用,也可以单独拿出来使用.不过有一点需要注意的是:在创建“请求上下文”时一定要创建一个“应用上下文”对象.有了“应用上下文”对象,便可以很容易地确定当前处理哪个应用,这就是魔法current_app
.在0.1版本中,current_app是对_request_ctx_stack.top.app的引用,而在0.9版本中current_app是对_app_ctx_stack.top.app的引用.其中_request_ctx_stack和_app_ctx_stack分别是存储请求上下文和应用上下文的栈
请求上下文: request、session
应用上下文: g、current_app
实现高并发
上面说了flask自带的wsgi的性能不好,一般都会选择其它能够实现高并发的http server,如gunicorn,greenlet,uswgi.
其中gunicorn几乎不需要什么配置就可直接整合到flask项目中,但是gunicorn只支持unix,而uswgi的配置比较复杂,但是性能比gunicorn稍好.
上面启动flask是直接在主函数中使用app.run(),这里我们结合gunicorn启动app(假设已pip install gunicorn)
gunicorn基于‘pre-fork worker’模型,意味着有一个中心主控master进程,用它来管理一组worker进程.
worker进程可以支持不同的IO方式(sync,gevent,eventlet,tornado等)
命令非常简洁:
1 | gunicorn -D -w 4 -b 127.0.0.1:4000 myproject:app |
其中:
- -D: 以deamon形式后台运行
- -w: 指定work进程数为4
- -b: 指定ip 和端口
- myproject:app 程序入口,前为工程入口文件,后为flask启动的应用
这样flask就以gunicorn的方式启动了,并发数也大大提高.
gunicorn处理静态资源的能力不是很好,所以一般都会在前面再部署个nginx,只需要将proxy_pass 指向gunicorn的监听端口即可,可以参考这里
更多的flask实现高并发部署方式请见这里