Flask 细节总结

断断续续的在看 Flask 的源码, 记录下一些有意思的现象。一些来自网络,一些是自己发现。

jsonify 和 json.dumps 的区别

  1. Content-Type有区别: 使用 jsonify 时响应的 Content-Type 字段值为 application/json,而使用 json.dumps 时该字段值为 text/html

  2. jsonify 输出的数据量更小,因为非 debug 模式或者未设置 JSONIFY_PRETTYPRINT_REGULAR 会使返回更加紧凑。

在 Debug 模式下初始化两次

1
2
3
4
5
6
from app import app
import time

if __name__ == '__main__':
print(time.time())
app.run(port=5000, debug=True)

输出

1
2
3
4
5
6
1492742262.002537
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
1492742262.598912
* Debugger is active!
* Debugger pin code: xxx-xxx-xxx

这将导致我们的某些需要在初始化时执行的方法被执行2次,这显然不是我们需要的结果。出现这样的问题的原因是在开启 Debug 模式的时候,Werkzeug 默认会 启动一个额外的进程 来监控文件变化以方便重启进程。

要解决这个启动两次的问题,有这样几种方法:

1.取消自动重启

1
app.run(port=5000, debug=True, use_reloader=False)

2.判断 Werkzeug 主进程是否执行

restart_with_reloader function 中,我们可以看到在新进程启动前,环境变量 WEAKZEUG_RUN_MAIN 被置为 'true'

1
new_environ['WERKZEUG_RUN_MAIN'] = 'true'

1
2
3
4
5
if __name__ == '__main__':
import os
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
print(time.time())
app.run(port=5000, debug=config.DEBUG)

3.在第一次请求的时候执行

使用 before_first_request 这个钩子,把执行放在 Flask 第一次收到请求的时候。这就避免了2次初始化的干扰。

1
2
3
@app.before_first_request
def initialize():
print(time.time())

控制上传文件的大小

1
2
3
4
from flask import Flask, Request

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

为什么Gunicorn 和 Nginx 一起使用

Nginx功能强大,使用Nginx有诸多好处,但用Nginx转发Gunicorn服务,重点是解决“慢客户端行为”给服务器带来的性能降低问题;另外,在互联网上部署HTTP服务时,还要考虑的“快客户端响应”、SSL处理和高并发等问题,而这些问题在Nginx上一并能搞定,所以在Gunicorn服务之上加一层Nginx反向代理,是个一举多得的部署方案。

Gunicorn 是一个pre-forking (会通过预先开启大量的进程,等待并处理接到的请求。由于采用了这种方式来开启进程,服务器并不需要等待新的进程启动而消耗时间,因而能够以更快的速度应付多用户请求。) 的软件,这类软件对低延迟的通信,如负载均衡或服务间的互相通信,是非常有效的。但pre-forking系统的不足是,每个通信都会独占一个进程,当向服务器发出的请求多于服务器可用的进程时,由于服务器端没有更多进程响应新的请求,其响应效率会降低。

所以把Nginx挡在pre-forking服务前面处理请求是一种很好的选择。Nginx能够异步、高并发的响应客户端request(慢客户端请求对Nginx影响不大),Nginx一旦接收到的请求后立刻转给Gunicorn服务处理,处理结果再由Nginx以response的形式发回给客户端。这样,整个服务端和客户端的通信,就由原来仅通过Gunicorn的同步通信,变成了基于Nginx和Gunicorn的异步通信,通信效率和并发能力得到大大提升。

sqlalchemy 实现时间列自动更新

1
2
3
4
5
6
class MonitorData(Base):
__tablename__ = 'monitor_data'
id = Column(Integer, primary_key=True)
uid = Column(String(100))
gmt_create = Column(TIMESTAMP(True), server_default=func.now()) # 创建时间
gmt_modify = Column(TIMESTAMP(True), nullable=False, server_default=func.now(), onupdate=func.now()) # 修改时间,onupdate设置在其他自动更新的时候,自动变化。nullable是该字段是否可以为空