這是國外一個(gè)專門做框架性能對(duì)比的網(wǎng)站(TechEmpower)的數(shù)據(jù),這是對(duì) Python 語言的常用 web 框架的測試報(bào)告,可見 fastapi 表現(xiàn)優(yōu)異。

這是詳細(xì)的鏈接,有興趣的可以看下:

除了在上邊數(shù)據(jù)方面的優(yōu)異表現(xiàn)外,它還具有如下特點(diǎn):

拋去這些,我覺著僅性能堪比 Golang 語言的 gin 框架這一條,就值得我們學(xué)習(xí)下。

下面我們來具體看下 FastApi 的使用。

FastApi 知識(shí)預(yù)備

在看 FastApi 之前,我們先來回憶下 Python 的異步編程和協(xié)程。

Python 的異步編程

Python 1.0 發(fā)布在 1994 年,當(dāng)時(shí)對(duì)編程語言性能的要求并不像現(xiàn)在這么高,所以它在設(shè)計(jì)之初就更傾向于快速開發(fā)和便捷的語法。即便有性能的要求,多進(jìn)程多線程也完全能夠應(yīng)付。但是隨著現(xiàn)代互聯(lián)網(wǎng)的發(fā)展,對(duì)編程語言自身的性能要求就越來越高。除了多進(jìn)程和多線程模型,還逐漸演化出「協(xié)程」模型(協(xié)程,是一種用戶態(tài)的更小力度的任務(wù)運(yùn)行單元?;诰€程,可實(shí)現(xiàn)多個(gè)任務(wù)的并行。)為代表的異步編程思想。

在早期,Python 實(shí)現(xiàn)異步編程,需要地方的庫,如 Twisted、Tornado 等。從 Python3 開始,加入了對(duì)異步編程的原生支持。

下面這種以async def定義函數(shù),且用await?關(guān)鍵字來調(diào)用函數(shù)的代碼,便是協(xié)程相關(guān)的代碼。

import asyncio

async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y

async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

協(xié)程的運(yùn)行有 3 種方式,參考[5]

一些其他概念:

CGI(通用網(wǎng)關(guān)接口, Common Gateway Interface),簡單來說就是解析瀏覽器等客戶端發(fā)送給服務(wù)端的請求,并組裝需要返回的 HTTP 請求的一種通用協(xié)議,處理這個(gè)過程的程序,我們就可以叫 CGI 腳本?;ヂ?lián)網(wǎng)早起的動(dòng)態(tài)網(wǎng)頁都是基于 CGI 標(biāo)準(zhǔn)的。

WSGI?是一種 Python 專用的 Web 服務(wù)器網(wǎng)關(guān)接口,它分為兩部分”服務(wù)器(或網(wǎng)關(guān))“和”應(yīng)用程序(或應(yīng)用框架)”?!阜?wù)器」,一般獨(dú)立于應(yīng)用框架,為應(yīng)用程序運(yùn)行提供環(huán)境信息和一個(gè)回調(diào)函數(shù)(Callback Function)。當(dāng)應(yīng)用程序完成處理請求后,透過回調(diào)函數(shù),將結(jié)果回傳給服務(wù)器。常用的 WSGI 服務(wù)器有: uwsgi、gunicon?!笐?yīng)用程序」,是各種實(shí)現(xiàn)了 WSGI 標(biāo)準(zhǔn)的 Python web 框架了,常用的有 Django、Flask 等。

ASGI(Asynchronous Server Gateway Interface)?是 Django 團(tuán)隊(duì)提出的一種具有異步功能的 Python web 服務(wù)器網(wǎng)關(guān)接口協(xié)議。能夠處理多種通用的協(xié)議類型,包括 HTTP,HTTP2 和 WebSocket。WSGI 是基于 HTTP 協(xié)議模式的,不支持 WebSocket,而 ASGI 的誕生則是為了解決 Python 常用的 WSGI 不支持當(dāng)前 Web 開發(fā)中的一些新的協(xié)議標(biāo)準(zhǔn)(WebSocket、Http2 等)。同時(shí),ASGI 向下兼容 WSGI 標(biāo)準(zhǔn),可以通過一些方法跑 WSGI 的應(yīng)用程序。常用的「服務(wù)器」有 Daphne、Uvicorn。

FastApi 快速開始

Hello world

FastApi 要求 Python 在 3.6 以上,它應(yīng)用了很多 Python3 的新特性。下面我們安裝一下:

pip install fastapi uvicorn

我們使用uvicorn來啟動(dòng)異步的 web server ,同時(shí)把uvicorn也安裝上。

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

uvicorn 有兩種啟動(dòng)方式:

if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

整體代碼看下來,和 Flask 的 Hellow world 類似,有程序?qū)嵗?app 用來啟動(dòng)程序,有路由和路由處理函數(shù)。

我們來看下它自帶的 Swagger 類型的文檔:

是不是很方便。

接下來,讓我們來看些復(fù)雜點(diǎn)的用法。

日常使用

使用 get 方法來請求數(shù)據(jù):

@app.get('/user')
async def user(user_id: int = Query(..., title="The ID of the user to get", gt=0)):
# do somethings
return {'user_id': user_id}

QueryString 的傳遞使用了一個(gè)類Query來做參數(shù)的校驗(yàn),Query 類繼承了相關(guān) pydandic 庫的類,實(shí)現(xiàn)了對(duì)參數(shù)的類型校驗(yàn),附默認(rèn)值等功能。

此處...是表示 user_id 參數(shù)時(shí)必須的。若此處為 None,則表示 user_id 可選。

使用 put 方法來更新數(shù)據(jù):

@app.put('/user/{user_id}')
async def user(user_id: int = Path(..., title="The ID of the user to get", gt=0)):
# do somethings
return {'user_id': user_id}

這里的路徑參數(shù),可以使用類Path來做類型校驗(yàn),功能和Query類似。

使用 post 方法來創(chuàng)建數(shù)據(jù):

from pydantic import BaseModel

class User(BaseModel):
name: str
age: int

@app.post("/users/")
async def create_user(user: User):
# do somethings
return user

這里定義了一個(gè)用戶類,作為函數(shù)參數(shù)的類型。在請求時(shí),F(xiàn)astApi 會(huì)自動(dòng)的將參數(shù)注入到該類型的各對(duì)應(yīng)屬性字段。該類也起到了參數(shù)類型校驗(yàn)的作用。在函數(shù)參數(shù)類型這塊,User類有點(diǎn)類似 Golang 中結(jié)構(gòu)體定義的函數(shù)參數(shù)類型。

使用 delete 方法來刪除數(shù)據(jù):

@app.delete('/user/{user_id}')
async def user(user_id: int = Path(..., title="The ID of the user to get", gt=0)):
# do somethings
return {'user_id': user_id}

同 put 方法的用法。

看了這些基本的用法之后,我們對(duì) FastApi 的使用有了一個(gè)初步的了解。

我們從之前的代碼中可以看到路由和路由函數(shù)在一塊的,這種方式在路由少的情況下是非常方便的,但是隨著項(xiàng)目復(fù)雜度的提升,路由逐漸增多,如果路由設(shè)計(jì)不合理,便會(huì)非常的不好維護(hù)。

針對(duì)這種情況,F(xiàn)astApi 提供了 APIRouter類來規(guī)劃設(shè)計(jì)路由,以便適應(yīng)大型的項(xiàng)目架構(gòu)。

路由分解和版本管理

APIRouter?類的基本用法如下:

# 頁面路由
page_routes = APIRouter()
page_routes.include_router(views.router, prefix="")

# api 相關(guān)路由
api_routes = APIRouter()
api_routes.include_router(api_v1_views.router, prefix='/v1')
api_routes.include_router(api_v2_views.router, prefix='/v2')

可以通過它來拆分管理路由,最后再將所有路由注冊到根路由即可。

app = FastAPI()
app.include_router(page_routes)
app.include_router(api_routes, prefix=config.API_PREFIX)

一個(gè)真實(shí)的項(xiàng)目實(shí)例

接下來,讓我們看一個(gè)真實(shí)的生產(chǎn)環(huán)境中的項(xiàng)目案例:

我們從下邊幾點(diǎn)來詳細(xì)說下這個(gè)項(xiàng)目案例:

1/ ORM

首先在 ORM 選擇方面,官方推薦了強(qiáng)大的SQLAlchemy,它可以說是 Python web 開發(fā)中最好用的第三方 ORM 了。在同步框架 Flask 中,應(yīng)該說已經(jīng)成為標(biāo)配。

SQLAlchemy 目前為止對(duì)異步的支持還不夠完成,官方推薦使用 Encode 組織下的異步驅(qū)動(dòng) databases(目前支持 PostgreSQL/MySQL/SQLite),只所以叫他驅(qū)動(dòng)沒叫他 ORM,是因?yàn)樗鼉H提供了和數(shù)據(jù)庫的異步鏈接的管理,并沒有對(duì)象模型。在使用的時(shí)候,可以結(jié)合SQLAlchemy或直接寫 SQL。

使用SQLAlchemy來處理同步的數(shù)據(jù)操作,使用SQLAlchemydatabases來實(shí)現(xiàn)異步的數(shù)據(jù)操作,完美實(shí)現(xiàn)了我們的需求。

2/ 數(shù)據(jù)庫遷移

SQLAlchemy 還有一個(gè)數(shù)據(jù)庫遷移的問題,它自己不支持?jǐn)?shù)據(jù)庫表的變更遷移,只能刪除重建。在 Flask 中可以使用插件flask_migrate來實(shí)現(xiàn)數(shù)據(jù)庫表的變更遷移。

SQLAlchemy官方推薦使用alembic。一個(gè)遷移過程大概步驟如下:

# 1/ 初始化環(huán)境
alembic init migrations

# 2/ 修改配置參數(shù)

# migrations/env.py
import sys
sys.path = ['', '..'] + sys.path[1:]

from core.config import DATABASE_URL
config.set_main_option("sqlalchemy.url", str(DATABASE_URL))

...
from models.posts import PostsBase
from models.posts2 import PostsBase2
target_metadata = Base.metadata # 一個(gè)app model
target_metadata = [PostsBase.metadata, PostBase2.metadata] # 多個(gè)app model

# 3/ 生成遷移腳本
alembic revision --autogenerate -m "init"

# 4/ 應(yīng)用遷移腳本到數(shù)據(jù)庫
alembic upgrade head

3/ service 拆分

從項(xiàng)目目錄可以看到有個(gè) service 目錄,很顯然,當(dāng)業(yè)務(wù)邏輯服務(wù)度升高時(shí),我們可以提取很多共用的邏輯作為底層的共用邏輯。這個(gè)就比較靈活了,完全有你自己控制。各種設(shè)計(jì)模式,就可以往上懟了。

4/ Docker 化

最后我們來看下 docker 化,Dockerfile 如下:

# 我們選擇了官方 slim 鏡像,體積相對(duì)小
FROM python:3.9-slim-buster

LABEL maintainer="DeanWu <pyli.xm@gmail.com>"

# stdout 無緩沖,直接輸出
ENV PYTHONUNBUFFERED 1

# 復(fù)制代碼,調(diào)整工作目錄和腳本權(quán)限
COPY . /app

WORKDIR /app

RUN chmod +x start.sh prestart.sh start-reload.sh

# 安裝用到的工具和python 包
RUN apt-get update && \
apt-get install -y --no-install-recommends default-libmysqlclient-dev gcc libffi-dev make && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
pip install --no-cache-dir -r requirements.txt && \
rm -rf requirements.txt && \
pip install --no-cache-dir gunicorn

ENV PYTHONPATH=/app

EXPOSE 80

# 啟動(dòng)命令 可添加也可不添加
#CMD ["sh", "start.sh"]

編譯和啟動(dòng),

docker build -t fastapi-mysql:v1.0 .

docker run -p 80:80 -d -e DB_CONNECTION="mysql://root:Root1024@xxxx/fastapi" fastapi-mysql:v1.0 ./start.sh

總結(jié)

到這里我們從背景到生產(chǎn)項(xiàng)目案例,已經(jīng)介紹完FastApi?這個(gè)框架了,你有沒有覺著這個(gè)框架很酷呢?我今天只是簡單的介紹了框架的部分應(yīng)用場景,還有很多好玩的功能,大家可以去參考它的官方文檔。也可以關(guān)注我公眾號(hào),一起討論學(xué)習(xí)。

本文章轉(zhuǎn)載微信公眾號(hào)@碼農(nóng)吳先生

上一篇:

Ocelot:.NET開源API網(wǎng)關(guān)提供路由管理、服務(wù)發(fā)現(xiàn)、鑒權(quán)限流等功能

下一篇:

Java 快速開發(fā)框架 magic-api
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場景實(shí)測,選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)