株式会社メドレーDeveloper Portal

2021-02-01

HTTP コンテンツ圧縮でパフォーマンス改善

事業本部 プロダクト開発室のエンジニアの中畑です。

オンライン診療・服薬指導・クラウド診療支援システム「CLINICS」の開発・基盤周りを担当しております。

今回は、HTTP のコンテンツ圧縮について調査・対応する機会があったので、本ブログにて紹介したいと思います。

HTTP コンテンツの圧縮とは

HTTP コンテンツの圧縮とは、HTTP の通信において Web サーバー側が返すデータを、なんらかの形式で圧縮してクライアントに返すことです。圧縮されたレスポンスをクライアント側は解凍して利用します。

HTTP コンテンツの圧縮によって得られるメリット・デメリットは以下の通りです。

⤴ メリット

  • 通信の帯域使用量を減らせる
  • それによって通信にかかる時間を削減し、ページ表示速度を向上できる

⤵ デメリット

  • 圧縮・解凍コストがかかる
    • ただし、圧縮・解凍コストはほとんどの場合は小さいため、メリットを下回る
  • 大容量ファイルやもともと圧縮されているファイル(画像や動画、PDF ファイルなど)を圧縮するのは、圧縮してもサイズがそれほど小さくならないため非効率である
    • サイズがあまり削減できない割に、圧縮・解凍に CPU リソースを使い、数百 MB を超えるファイルになるとそれぞれ数秒かかることもある

HTTP コンテンツを圧縮するためには

HTTP コンテンツを圧縮するためには、クライアントが解凍可能な圧縮形式を指定する必要があります。解凍可能な圧縮形式を指定するには、リクエストヘッダにAccept-Encodingヘッダを指定します。

最近のブラウザでは、HTTP リクエスト時に自動的にAccept-Encodingヘッダを自動的に付加してアクセスしているので、ブラウザ経由の場合は特に明示的に指定する必要はありません。Chrome, Safari, Edge など、ほとんどのメジャーなブラウザではAccept-Encoding: gzip, deflate, brが指定されています(※2021-01-23 時点)。

圧縮形式(gzip, deflate, br)

圧縮形式はいくつかありますが、ブラウザを利用する場合は以下のいずれかが選択肢になります。

  • gzip: LZ77 と 32 ビット CR を用いた圧縮形式
  • deflate: zlib 構造体と deflate 圧縮アルゴリズムを用いた圧縮形式
  • br: Brotli アルゴリズムを用いた圧縮形式。gzip に近いが大容量の言語辞書を用いて、頻出するパターンの単語を圧縮して効率化。そのため文章的なテキストではgzip よりも圧縮率が高いと言われる

Brotliは比較的新しい形式で、ほとんどのサーバー、ブラウザで対応しています。

サーバーでの HTTP コンテンツの圧縮方法(gzip)

サーバーはクライアントのAccept-Encodingリクエストヘッダを受け取り、その中から 1 つを選択して圧縮処理を行い、Content-Encodingレスポンスヘッダを付加してクライアントに結果を知らせます。

20210129123916.png

CLINICS が利用しているそれぞれのアプリケーション・ミドルウェアに絞って、どのように HTTP コンテンツ圧縮を実現しているか解説したいと思います。いくつか圧縮形式はありますが、ここでは gzip 形式での圧縮方法について解説します。

NGINX

NGINX のngx_http_gzip_moduleを利用することで gzip 圧縮することができます。

nginx.conf のgzipディレクティブをonにすることで圧縮が有効になります。ただし、タイプを指定しないとContent-Type: text/htmlのときにしか圧縮されません。他のタイプでも圧縮したいときはgzip_typesディレクティブも合わせて指定する必要があります。gzip_types*を指定することで、すべてのコンテンツを圧縮することもできます。

gzip: on;
gzip_types: text/css application/javascript application/json

また、CloudFront など Proxy を経由してのアクセスの場合はデフォルトでは行われません。Proxy 経由のアクセスかどうかは、リクエストヘッダにViaヘッダがあるかどうかで判定します。

CloudFront 経由でのアクセスの場合はVia: 1.1 xxxxx.cloudfront.net (CloudFront)のようにViaヘッダが付加されているため、NGINX にて Proxy 経由であると判定します。Proxy 経由であっても何かしらの条件で圧縮したい場合はgzip_proxiedディレクティブを指定する必要があります。

ref. https://nginx.org/en/docs/http/ngx_http_gzip_module.html

CloudFront

CloudFront の Behavior の設定にて設定します。Compress Objects Automatically を有効化することで、gzip 圧縮が有効になります。

20210129124017.png

上記を有効化すると、CloudFront では以下の条件で圧縮が行われます。

  • ファイルサイズが 1,000(≒1KB) 〜 10,000,000(≒10MB) バイトの間
    • よって、オリジンからファイルサイズを判定するためのContent-Lengthヘッダが付与されていない場合は、サイズ判別できないため圧縮されない
  • 特定のContent-Typeのコンテンツを圧縮する
    • テキスト系のコンテンツは圧縮するが、画像や動画、PDF など、もともと圧縮されているものは対象外。詳しくはこちら
  • オリジン側(NGINX や Rails など)から圧縮して返される場合は、再度圧縮は行わない
    • Content-Encodingヘッダの有無で判定している

ref. https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html

Rails

Rails はデフォルトでは HTTP コンテンツの圧縮は行いません。Rails でコンテンツ圧縮を行いたい場合は、Rails の Rack Middleware のRack::Deflaterを導入するのが簡単です。しかしながら、Rack::Deflater はすべてのContent-Typeのコンテンツでも圧縮するので、画像や動画・ PDF など圧縮するべきでないコンテンツまで圧縮してしまいます。NGINX や CloudFront など、Rails 外の他のサービスやミドルウェアに任せるのが良いと思います。

CLINICS での HTTP コンテンツ圧縮のシーケンス

前章で解説したアプリケーション・ミドルウェアは、CLINICS では以下のように連携しています。

20210129132442.png

AWS 上に Rails アプリケーションをデプロイしており、通常のアクセスはロードバランサーから NGINX を経由して Rails にアクセスし、静的ファイルなどキャッシュコンテンツは CloudFront 経由でアクセスしています。

CLINICS では用途に合わせた圧縮を行っています。3 つのケースを紹介します。

1. NGINX 経由で Rails にアクセスした時

20210129124126.png

API アクセスなどは上記シーケンスでアクセスしています。ほとんどがtext/htmlapplication/json形式のコンテンツとなり、NGINX にて gzip 圧縮処理を行っています。Rails はアプリケーションの処理のみを行い、圧縮は行わないようにしています。

2. CloudFront 経由で S3 にアクセスした時

20210129124148.png

画像ファイルや PDF、静的な js、css ファイルなどはサービスのデプロイ時に S3 にアップロードしています。クライアントは CloudFront 経由でアクセスし、S3 から取得して、CloudFront で gzip に圧縮処理を行っています。また、一定期間 CloudFront 上にキャッシュされるので、効率よく圧縮コンテンツを返します。

3. CloudFront→NGINX→Rails 経由で S3 にアクセスした時

20210129124210.png

静的ファイルの中でもシグネチャをチェックしているものは、このフローでアクセスしています。NGINX でも圧縮設定を ON にしていますが、Viaヘッダがあるため、NGINX では圧縮しないようになっています。

まとめ

HTTP コンテンツの圧縮を適切に行うことで、サービス全体のパフォーマンス向上が見込めます。更に CloudFront を活用することで、アプリケーションやミドルウェアでの圧縮処理をなくし、更なるパフォーマンス向上が見込めます。

今回は HTTP コンテンツの gzip 圧縮についてのみ触れましたが、Brotli 圧縮についても NGINX、CloudFront ともに可能なため、今後取り入れていきたいと考えています。もし HTTP コンテンツの圧縮設定を特に気にしたことがない方は一度確認してみてはいかがでしょうか?

最後に

メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しています。医療という分野においては、機微な情報を扱ったり診療を止めないようにするために、パフォーマンス・セキュリティ共に高いサービスレベルが求められます。興味を持った方がいらっしゃいましたら、まずは気軽に面談できればと思いますので、是非ご応募ください!

最後までお読みいただきありがとうございました。

https://www.medley.jp/jobs/

株式会社メドレーDeveloper Portal

© Medley Developer Portal