Pycon 2016 将于今天谢幕了,在过去的几天里,Python 大神们分享了大量自己关于 Python 在各方面的见解以及实践,作为还处于大量积累能量的层次,我也是期待着这次大会的大神们的分享,很高兴的是本次 pycon 大会没有令我失望,有几个分享让我有种浴血喷张的感觉,其中一个就是 MG(Miguel Grinberg) 大神(就是那个引我 Flask 入门的《Flask Web开发》的作者)分享的 《Flask At Scale》(我翻译为 《Flask 大规模实践》)。

MG 的《Flask At Scale》我觉得可以分为两部分,分别是:

那本文就以个人的视角给大家分享一下我观看完 《Flask At Scale》的个人收获。

可维护的代码

前半部分的讲解和我在 《The Way To Flask》 中前 8 章介绍的差不多,都是不断地优化代码结构用的。

v0.1 版本的代码是一个非常简单的 Flask 应用:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()


整体代码结构是这样的:

flack/
├── flack.py
├── templates/
| └── index.html
├── static/
| └── client-side js and css files
├── tests.py
└── requirements.txt


v0.2 版本主要是抽离出了一个 utils 文件,现在整体项目结构变成:

flack/
├── flack.py
├── utils.py        <------ 新增的
├── templates/
| └── index.html
├── static/
| └── client-side js and css files
├── tests.py
└── requirements.txt


v0.3 版本抽离出了 Model,抽离之后的结构为:

flack/
├── flack.py
├── models.py       <------ 新增的
├── utils.py
├── templates/
| └── index.html
├── static/
| └── client-side js and css files
├── tests.py
└── requirements.txt


这里构建 Model 文件有个技巧,就是如何使用 SQLAlchemy 对象,作者推荐的是这样的:

try:
    from __main__ import db
except ImportError:
    from flack import db

class User(db.Model):
    pass


原因就是为了防止循环引用。

v0.4 版本是重构了整个项目的结构,引入了启动文件:

flack/
├── flack/
| ├── __init__.py
| ├── flack.py
| ├── models.py
| ├── utils.py
| ├── templates/
| | └── index.html
| └── static/
| └── client-side js and css files
├── tests.py
├── manage.py <- runserver, shell and createdb commands available here
└── requirements.txt


v0.5 版本的话是抽离出了验证授权的接口:

flack/
├── flack/
| ├── __init__.py
| ├── flack.py
| ├── auth.py           <------ 新增的
| ├── models.py
| ├── utils.py
| ├── templates/
| | └── index.html
| └── static/
| └── client-side js and css files
├── tests.py
├── manage.py
└── requirements.txt


v0.6 的重点在重构单元测试文件:

flack/
├── flack/
| ├── __init__.py
| ├── flack.py
| ├── auth.py
| ├── models.py
| ├── utils.py
| ├── templates/
| | └── index.html
| └── static/
| └── client-side js and css files
├── manage.py <- test and lint commands added here
├── tests/
| ├── __init__.py
| └── tests.py
└── requirements.txt


v0.7 版本的话就是规整了配置文件,根据不同环境提供了不同的配置:

flack/
├── flack/
| ├── __init__.py
| ├── flack.py
| ├── auth.py
| ├── models.py
| ├── utils.py
| ├── templates/
| | └── index.html
| └── static/
| └── client-side js and css files
├── config.py
├── manage.py
├── tests/
| ├── __init__.py
| └── tests.py
└── requirements.txt


v0.8 的话就是创建 API Blueprint 了。。

flack/api.py
from .flack import db

api = Blueprint('api', __name__)

@api.route('/users', methods=['POST'])
def new_user():
    pass


v0.10 版本就是修改 app 的创建方式,引入了工厂方法:

db = SQLAlchemy()
def create_app(config_name=None):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    db.init_app(app)
    # ...
    return app


v0.11 就是创建了一个 API 包:

flack/
├── flack/
| ├── __init__.py
| ├── flack.py
| ├── auth.py
| ├── models.py
| ├── utils.py
| ├── api/
| | ├── __init__.py
| | ├── tokens.py
| | ├── messages.py
| | ├── users.py
| ├── stats.py
| ├── templates/
| | └── index.html
| └── static/
| └── client-side js and css files
├── config.py
├── manage.py
├── tests/
| ├── __init__.py
| └── tests.py
└── requirements.txt


这个版本的目录结构就是 MG 认为比较合理的 Flask 应用程序的结构,毕竟适合维护和扩展,但是,我认为还是有改进的空间的,例如 Models 还能抽离出一个目录,配置能抽离出一个目录,可能还会更具业务需要抽离出一个业务目录。但是,具体如何还需要各人结合自己的项目情况具体得取舍。

接下来就是性能方面的优化了,那就来看下 MG 大神在性能方面有哪些建议:

对于一个应用的性能来说,瓶颈可以概括得分为两块,分别是 IO 和 CPU 瓶颈,对于

当然,我们完全可以使用丢机器的方法解决一些初期的瓶颈问题,所以 v0.12 版本就是使用 Nginx 进行一个反向代理进行多机服务:

但是这也架不住很多 CPU 或者 IO 密集的任务啊,所以是该引入异步任务了,所以 v0.13-14 引入了 Celery ,

那现在遇到的问题就是客户端需要不断地轮训保持实时刷新,所以这个时候需要选择方案:

MG 这里选择是 选项4,所以这里变成了服务端主动推送的模型:

# server.py
def push_model(model):
 socketio.emit('updated_model', {
 'class': model.__class__.__name__,
 'model': model.to_dict()
 })
# client/js
Client (JavaScript)
    socket.on('updated_model', function(data) {
     if (data['class'] == 'User') {
     updateUser(data.model);
     }
     else if (data['class'] == 'Message') {
     updateMessage(data.model);
     }
});


将这三者结合起来,就成了作者最终介绍的形态:

这就是 MG 大神向我们展示的使用 Flask 构建大规模应用的策略,虽然他没说具体的效果如何,可以应对怎样场景多大规模的请求,但是确实让我学到了不少东西,可以给大家分享一下。

最后,附带上大神的视频国内链接:Flask At Scale 优酷