帯域の節約か、CPUの犠牲か? Web APIにおける圧縮アルゴリズムの「最適解」を求めて
ネットワークエンジニアとして現場を歩いていると、APIのパフォーマンスチューニングで必ずぶち当たる壁がある。それが「圧縮」だ。
「とりあえずGzipを有効にしておけばいい」と考えるのは、まだ初級レベル。APIのレスポンスサイズが数MBに達するような巨大なJSONを扱う際、圧縮アルゴリズムの選定ミスは、クライアント側のレイテンシ悪化や、サーバー側のCPU負荷増大という「二重苦」を招くことになる。
今回は、RFC 7231 (HTTP/1.1 Semantics) の仕様を紐解きつつ、実務で知っておくべき圧縮の勘所を伝授する。
—
1. Content-Encodingのメカニズム:パケットの裏側
HTTP圧縮の仕組みは至ってシンプルだ。サーバーは Content-Encoding ヘッダーを用いて、送信するデータのエンコーディング方式をクライアントに通知する。
通信の流れはこうだ。
1. ネゴシエーション: クライアントが Accept-Encoding: br, gzip, deflate ヘッダーを送り、「俺はこの形式を解凍できるぞ」と提示する。
2. 選択: サーバーは提示された中から最も効率的(あるいは負荷が低い)と判断したアルゴリズムを選択する。
3. 転送: 圧縮されたバイナリがTCPセグメントに乗って運ばれる。
4. 展開: クライアント(ブラウザやSDK)が Content-Encoding を見て、適切なアルゴリズムでデコードする。
このフローにおいて、エンジニアが意識すべきは「圧縮率」と「CPUコスト」のトレードオフだ。
—
2. 圧縮アルゴリズムの特性:Gzip vs Brotli
現在、実務で選択肢に上がるのは主に gzip と br(Brotli)の二つだ。
Gzip (Deflateベース)
- 特徴: 枯れた技術。ほぼ全ての環境でサポートされている。
- メリット: 圧縮・解凍の計算コストが低く、リアルタイム性が高い。
- デメリット: Brotliに比べると圧縮率で一歩劣る。
Brotli (Google製)
- 特徴: 静的コンテンツやJSONの圧縮に特化した高効率アルゴリズム。
- メリット: Gzipより圧縮率が15〜20%高い場合も珍しくない。特に「静的アセット」や「固定パターンの多いAPIレスポンス」で真価を発揮する。
- デメリット: 圧縮レベルを上げるとサーバーのCPU負荷が跳ね上がる。
結論: APIにおいては、動的なデータが多いなら gzip で安全を確保し、キャッシュが効くようなデータや巨大なJSONであれば br を検討するのが定石だ。
—
3. 実践:Nginxでの設定と検証
現場で最も多いケースであるNginxでの設定例を見てみよう。/etc/nginx/nginx.conf に以下の設定を追加する。
# 圧縮を有効化
gzip on;
gzip_types text/plain application/json application/javascript text/css;
# Brotliの設定 (要: ngx_brotli モジュール)
brotli on;
brotli_comp_level 6; # 圧縮レベルは1-11。6がCPUと圧縮率のバランスが最も良い
brotli_types text/plain application/json application/javascript;
設定後は、必ず curl コマンドでヘッダーを確認する。これがエンジニアの作法だ。
# Brotliが優先されるか確認
curl -I -H "Accept-Encoding: br" https://api.example.com/data
# 期待される出力:
# Content-Encoding: br
—
4. Pythonで書くクライアントサイドの挙動
フロントエンドやバックエンドからAPIを叩く際、requests ライブラリを使っていれば意識する必要はほとんどない。しかし、あえて明示的に指定して挙動をテストするなら以下のようになる。
import requests
url = "https://api.example.com/data"
headers = {
"Accept-Encoding": "br, gzip" # サーバーに圧縮を要求
}
response = requests.get(url, headers=headers)
# 圧縮方式を確認
print(f"Used Encoding: {response.headers.get('Content-Encoding')}")
# 圧縮後のサイズを確認
print(f"Size: {len(response.content)} bytes")
—
5. 現場のシニアからのアドバイス:避けるべき罠
最後に、現場でよくある失敗を二つ挙げておく。
1. すでに圧縮されているデータへの二重圧縮:
画像(JPEG/PNG)や動画ファイルに gzip をかけてはいけない。これらは既に圧縮済みであり、二重圧縮はCPUの無駄遣いになるだけでなく、ファイルサイズが逆に増えることもある。gzip_types の設定には細心の注意を払うこと。
2. CPU負荷の過信:
「Brotliの圧縮レベルを最大(11)にすれば最高速になる」と考えてはいけない。レベル11は非常に高負荷であり、APIのレスポンスタイム(TTFB: Time To First Byte)を悪化させる可能性がある。動的APIであれば、レベル4〜6程度で留めるのが、長年運用を続けてきた中での経験則だ。
ネットワーク通信は「見えないもの」を扱う仕事だ。パケットのサイズ、CPUの負荷、クライアントのサポート状況。これらを多角的に俯瞰して設計する姿勢こそが、美しいAPIを支える土台となる。
今日のチューニングが、明日のユーザー体験を劇的に変える。ぜひ、現場のサーバーで Content-Encoding を見直してみてほしい。
コメント