2022 年构建现代 Python API 后端开发指南


本指南适用于已经熟悉 Python 3 并希望开始新项目的人。
本指南来自为不同行业的各种初创公司构建 api 后端的经验,它作为我们如何看待组织 Python 项目、结构化代码、测试和公共库的“最佳实践”状态的起点我们在项目中重复使用。
我们对本指南采取了一种有意的固执己见的方法,我们对事情应该如何工作有强烈的看法——这一切都是从从头开始构建多个 python 后端的经验教训中得到的。

构建 API 后端时默认使用这些库
FastAPI - HTTP 路由
这是我们在 Python 中编写请求/响应处理程序的首选库。它对异步、Python 3 类型具有一流的支持,并且文档很棒。

Pydantic - 数据序列化、反序列化和验证
这个库是从 FastAPI 序列化/反序列化请求和响应的推荐方式。我们使用它来将数据库模型序列化为正确的格式,以便作为响应发送回客户端。

SQLAlchemy - ORM
Python 生态系统中最好的 ORM,可能还有其他语言。

Pytest - 单元和集成测试
目前最好的 Python 测试框架

Celery - 分布式任务队列
Python 生态系统中最好的软件之一。我们已经使用它在多个节点上运行数百万个后台作业。当你开始扩展时,你最终会在你的堆栈中的某个地方使用它。

Structlog - 结构化日志记录
这是我们工具带中最近添加的内容之一。这个库比标准的日志库要好得多。

Typer - 构建 CLI 脚本
FastAPI 作者的另一个创作,它广泛使用 Python 类型来构建 CLI 应用程序。我们强烈推荐这个而不是标准argparse库

Alembic - 管理数据库迁移
这与 SQLAlchemy 一起努力管理您的数据库模式。如果您使用的是数据库,这是管理模式版本控制的关键软件。

Poetry - 依赖管理
如果你还在使用requirements.txt你做错了。Poetry 是目前 Python 最好的包管理器。它的工作方式类似于 yarn,有一个poetry.lock文件跟踪安装的库的具体版本,另一个pyproject.toml跟踪安装的依赖项。

Python 类型是你的朋友,尽可能地使用它们
你的代码库应该利用Python 3中引入的类型的优势。通过一个正确设置的编辑器,比如visual studio code,当类型不匹配或可选类型没有被正确检查时,它会提供有用的提示。

很多流行的Python库现在都带有类型定义,像FastAPI这样的框架也利用了类型信息,现在没有理由不使用它们了。

如果你使用Python 3.9以上版本,请使用内置的类型,即list、dict等,而不是使用类型库中的对应类型。

尽可能使用自动化来保持你的代码库的清洁
我们使用black、isort、mypy和flake8与vscode的集成,以确保我们将良好的代码运送到生产中。

Black和isort确保代码的格式正确,并且在我们的代码库中保持一致。这些工具的默认设置是有意见的,但这是件好事。

Mypy和flake8可以捕捉到未使用的变量或混合类型等问题。将这些工具整合到你的CI流程中,或者在vscode本身中,可以在编写代码时及早发现问题。

当代码库开始增长时,保持所有东西 "看起来 "的一致性很重要。它使现有的开发人员更容易阅读代码,而新的开发人员则更容易浏览代码库。

你的后端代码将在很大程度上属于这些类别。
在大多数后端项目中,你的应用程序代码将落入这些类别中。

  • 数据库访问 - 基本上是对数据库的CRUD操作
  • 服务--任何有业务逻辑的东西,特定于你的应用程序领域的东西,或任何与外部服务互动的东西
  • 路由--在我们的例子中,FastAPI端点,处理请求和发送回复的代码。
  • 模式--将模型序列化为json响应,并将请求反序列化为python类,包括请求数据的验证。
  • 任务 - 在你的工作队列中运行的异步任务
  • 脚本 - 操作任务,如删除用户、回填数据、运行迁移等
  • 测试 - 单元和集成测试

保持文件夹结构简单
为了简化代码库的导航,我们通常像这样构建我们的 Python 后端:

.
├── app
│   ├── models (Your ORM models)
│   ├── repos (Database CRUD classes)
│   ├── routers (FastAPI endpoints)
│   ├── schema (Pydantic schemas)
│   ├── services (Your domain code)
│   ├── tasks (Celery tasks)
│   └── lib (shared code)
├── migrations (Alembic database migrations)
├── scripts (Various CLI scripts)
└── tests (Unit and integration tests)


我们发现这个文件夹结构非常适合组织我们的 python 后端代码库。很容易确定一段特定的代码在哪里,对于任何创建新文件的人来说,应该在哪里创建文件是很清楚的。

少用SQLAlchemy的 "神奇 "功能
让我先说一下,在编写与数据库交互的代码时,大多数ORM都能解决90%的问题。尤其是SQLAlchemy是我所使用的最好的ORM。

当你对你的数据库进行简单的查询时,ORM可以节省时间,但是当你有一个复杂的数据模型,需要更高级的查询结构时,你最终会试图找出如何将你想要构建的SQL查询映射到SQLAlchemy的方式。SQL已经是一种强大的语言,很多时候,编写原始SQL查询会更好。

我遇到的另一个问题是,在创建SQLAlchemy模型时,你有大量的选项。例如,在定义模型之间的关系时的各种选项。在SQLAlchemy手册中,有一整章专门讨论关系配置。这意味着必须保持一个关于SQLAlchemy如何工作以及普通SQL如何工作的心理模型。

在使用SQLAlchemy时,我们的经验法则是使用SQLAlchemy ORM的一小部分智能功能。我们避免通过ORM关系添加/删除模型,避免使用多态模型等,基本上任何与SQL相去甚远的东西。

容器化你的后端
对于本地开发,我们有一个单一的docker-compose文件,它将带来整个后端堆栈,包括所有相关服务。这使得开发人员的体验是一流的。开发人员能够进行修改,探查api端点,并查看他们的修改是否符合预期,而不必等待代码被运送到测试环境。

拥有一个能强烈反映生产环境运行情况的本地设置是提高开发人员生产力的最大胜利之一。

将你的代码打包在容器中,现在是在2022年将代码运送到生产中的默认方式。大多数主机供应商都有一些平台,允许你运行容器,无论是管理的k8s服务、AWS ECS、Google Cloud Run,还是Digitalocean的应用平台。我们在Digitalocean上使用管理的Kubernetes集群运行我们的后端。

使用结构化的日志
你应该在生产中使用日志。日志可以帮助你调试问题,所以不要对你的日志进行节约。日志存储很便宜,发射日志的性能开销也很低。

通过结构化的日志,你可以在日志行中嵌入ID、时间信息、错误细节等信息。如果你使用基于日志的指标产品,如google云日志,你可以提取指标或使用纯粹的日志输出来建立警报。

应用程序配置
按照12因素应用程序的惯例,我们将所有配置数据存储在我们运行的容器的环境变量中。

要获得一个配置变量,只需要调用os.getenv来访问变量的值。或者,你可以使用Pydantic的BaseSetting类,用环境中的值来填充一个类。

由于我们的后端应用程序使用的是kubernetes,我们使用ConfigMap来存储环境变量,然后由部署文件来引用。

简化你的操作/工程脚本的运行方式
运行和维护后端系统的一个不言而喻的事情是,你将积累各种脚本的集合,以执行频繁和临时的工程任务。

例如,如果你使用alembic来管理数据库迁移,你就需要对你的生产数据库执行alembic升级命令。如果你需要回填一些数据,你需要运行一个工程脚本来完成这个任务。

你要么是通过SSH进入一台可以访问生产的服务器来执行该脚本,要么是在你的本地机器上运行该脚本,该机器有访问生产的凭证。我们认为这两种方法都是不安全的,或者说是危险的,这就是为什么我们建立了Backfill,使在生产中执行脚本变得轻而易举。

结束语
希望本指南对指导你构建后端系统的方法有所帮助。

虽然本指南有一些针对Python的建议,但总的来说,它可以应用于大多数流行语言的后端建设。