tl;dr
FastAPI で responseをgzip圧縮で返却したい場合は
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000)
と記述する。
概要
HTTPでフロントエンドとの通信量を削減する方法の1つとして、コンテンツを特定の圧縮方式で圧縮して通信する方法がありますが、FastAPIでgzipによる圧縮が簡単にできるのでメモしておきます。
gzipでの通信のためには、
- リクエスト側がhttp headerの
Accept-encoding
属性に、gzipを指定する - サーバー側がgzipでの返却に対応する
の両方が必要です。
How To
公式ドキュメント にあるように、ミドルウェアを挟むだけでOKです。
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
BIG_CONTENT =
"""
返却するコンテンツ
"""
app = FastAPI()
# この行を追加する
app.add_middleware(GZipMiddleware, minimum_size=1000)
@app.get("/")
async def main():
return BIG_CONTENT
minimum_sizeで、圧縮を適用するコンテンツの最低サイズを指定することができます。
試してみる
上記のmiddlewareを追加した状態でAPIプロセスを起動させ、エンドポイントにcurlでリクエストしてみます。contentの内容はBible in Englishを利用しました。
まずはgzipをacceptしないでリクエストしてみます。
❯ curl -vv localhost:8000 > out
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Mon, 18 Apr 2022 08:41:39 GMT
< server: uvicorn
< content-length: 4457912
< content-type: application/json
<
{ [102270 bytes data]
100 4353k 100 4353k 0 0 158M 0 --:--:-- --:--:-- --:--:-- 202M
* Connection #0 to host localhost left intact
4457912 bytes返って来ているのがわかります。
それでは gzipを許可した上でリクエストしてみます
❯ curl -vv --compressed localhost:8000 > out2
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.77.0
> Accept: */*
> Accept-Encoding: deflate, gzip
>
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Mon, 18 Apr 2022 08:31:23 GMT
< server: uvicorn
< content-length: 1393083
< content-type: application/json
< content-encoding: gzip
< vary: Accept-Encoding
<
{ [102223 bytes data]
100 1360k 100 1360k 0 0 3924k 0 --:--:-- --:--:-- --:--:-- 3966k
* Connection #0 to host localhost left intact
Accept-Encoding: deflate, gzip
が指定されています。結果、レスポンスとして
content-encoding: gzip
content-length: 1393083
と変わっています。
この場合、4457912 bytes → 1393083bytes とおよそ70%ほどデータサイズが削減されていることがわかります。
余談: gzipにするべきか
通信容量だけ見ればgzipにすべきですが、当然圧縮・解凍のためのCPUリソースとはトレードオフになります。そのため、コンテンツのサイズが一定以上のもののみ圧縮対象とすると良い(minimum_size
の指定)と思います。
また、コンテンツ内容によっても圧縮率はだいぶ変わります。今回のテスト対象では70%ほど削減できましたが、画像等では圧縮の意味はほとんどないでしょう。
例えば「Can gzip Compression Really Improve Web Performance?」では、
- 事前に圧縮したファイルを配信しても、オンラインで圧縮してもそれほどパフォーマンスには大きな差はなかった
- HTMLやcombined JSなどは圧縮効果が高いものの、画像等では圧縮効果は小さい
- 容量が77%程度削減された結果、レスポンスタイムは15%ほど改善された
などの結果が出ていますので、特にサイズの大きなテキストコンテンツを扱うAPIで利用するとよさそうです。また APIではないですが、JSなど静的リソースもあらかじめ圧縮しておくとよさそうですね。