Medley Developer Blog

株式会社メドレーのエンジニア・デザイナーによるブログです

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レスポンスヘッダを付加してクライアントに結果を知らせます。

f:id:medley_inc:20210129123916p:plain

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圧縮が有効になります。

f:id:medley_inc:20210129124017p:plain

上記を有効化すると、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では以下のように連携しています。

f:id:medley_inc:20210129132442p:plain

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

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

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

f:id:medley_inc:20210129124126p:plain

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

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

f:id:medley_inc:20210129124148p:plain

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

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

f:id:medley_inc:20210129124210p:plain

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

まとめ

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

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

最後に

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

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

www.medley.jp