【実務・中級編】 HTTP圧縮アルゴリズム(Gzip, Deflate, Brotli)の選択とオーバーヘッド – Web APIアーキテクチャ・データ連携実践ガイド

帯域の節約か、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 を見直してみてほしい。

コメント

タイトルとURLをコピーしました