ハイパーテキスト転送プロトコル(HTTP)は、サーバーからクライアントにデータを転送する際のステートレスな要求応答モデルをサポートするように設計されています。最初のバージョンである1.0でサポートしていた要求と接続の比率は単に1:1でした(すなわち、1つの接続あたり1つの要求と応答のペアがサポートされていました)。
バージョン1.1では、この比率がN:1、つまり1つの接続あたり複数の要求に拡張されました。この拡張は、サーバーからクライアントに転送する必要がある多くのオブジェクトや要素を含め、Webページがますます複雑になっている状況に対処するためのものでした。
2.0では、HTTPは1つの接続あたり複数の要求のモデルを引き続きサポートしています。最も大きな変更点は、ヘッダーの交換とテキストベースの転送からバイナリへの移行です。
いつの間にか、HTTPは単にテキストや画像をサーバーからクライアントに転送するための単純な仕組みではなく、アプリケーションのためのプラットフォームとなっていました。ブラウザのユビキタス性、クロスプラットフォーム性、そして複数のオペレーティング システムや環境をサポートするために多額のコストをかけずにアプリケーションを展開できる容易さは、確かに魅力的でした。しかし残念ながら、HTTPはアプリケーション転送プロトコルとして設計されたものではなく、ドキュメントを転送するためのものでした。APIのようなHTTPの最新の用途でも、ドキュメント形式のペイロードを前提としています。その良い例がJSONで、そのデータ形式はキーと値のペアでありテキストとして転送されます。ドキュメントとアプリケーションのプロトコルは一般的にテキストベースですが、似ているのはそこだけです。
従来のアプリケーションは状態を維持するための何らかの方法を必要としますが、ドキュメントはそうではありません。アプリケーションは論理的なフローとプロセスを基盤としていますが、どちらもその時点でユーザーがどこにいるかをアプリケーションが認識している必要があり、状態を必要とします。HTTPの本質的にステートレスな性質にもかかわらず、HTTPはWebの事実上のアプリケーション転送プロトコルとなっています。そこでアプリケーションの使用中に状態を追跡できる手段としてHTTPに与えられたものが、これまでの技術の歴史の中で最も広く受け入れられた便利なツールの1つであることに間違いありません。その「ツール」として登場したのが、セッションとCookieです。
セッションは、Webサーバーとアプリケーション サーバーが状態を維持するための方法です。これらの単純なメモリの塊が、Webサーバーやアプリケーション サーバーに対して確立されるTCP接続ごとに関連付けられ、HTTPベースのアプリケーションで情報のインメモリ ストレージとして機能します。
ユーザーが初めてサーバーに接続すると、セッションが作成され、その接続に関連付けられます。開発者はそのセッションを、アプリケーション関連データの断片を保存する場所として使用します。このデータは、顧客IDなどの重要な情報から、サイトのトップ ページの表示方法の設定などあまり重要ではないデータまで多岐にわたります。
セッションの有用性を示す最も良い例がショッピング カートです。なぜなら、ほぼ誰もが一度はオンラインで買い物をしたことがあるでしょうから。ショッピング カートに入れられた商品はすべて、サーバー上の「セッション」に何らかの形で表されているため、そのセッションの間、ショッピング カートの商品は維持されます。また、もう1つの良い例として、ウィザード スタイルの製品設定アプリケーションやカスタマイズ アプリケーションがあります。これらの「ミニ」アプリケーションでは、一連のオプションを見て回り、選ぶことができます。そして最後に、追加したさまざまなものの合計金額に驚くことになります。それぞれの「オプションの画面」でクリックすると、選択した他のオプションがセッションに保存され、それらを簡単に表示、追加、削除することができます。
最新のアプリケーションはステートレスとして設計されていますが、それらのアーキテクチャはその原則に従っていない場合があります。最新の拡張方法はシャーディングなどのアーキテクチャ パターンに依存している場合が多く、ユーザー名、アカウント番号など、インデックス可能なデータに基づいて要求をルーティングする必要があります。したがって、ルーティングとアプリケーションの適切な動作を保証するために、インデックス可能なデータが各要求と一緒に運ばれるという、ある種のステートフルなアプローチを必要とします。この点で、最新の「ステートレス」なアプリケーションやAPIはしばしば、ステートフルな従来のアプリケーションと同程度の注意とフィードを必要とします。
問題は、セッションが接続に関連付けられていることと、接続が長い時間アイドル状態になっているとタイムアウトしてしまうことです。また、接続の「長い時間」の定義は、セッションにおける定義とはかなり異なります。例えば、一部のWebサーバーのデフォルトの設定では、接続がアイドル状態になる、つまり15秒間要求が作成されないと、その接続は閉じられます。一方、そのようなWebサーバーのセッションは、デフォルトでは300秒間、すなわち5分間メモリに保持されます。明らかにこの2つは対立しています。接続がタイムアウトしているのに、その接続に関連付けられたセッションがあっても何の意味があるのでしょうか?
セッションに合わせて接続のタイムアウト値を大きくして、この不一致を解消すれば良いと思うかもしれませんが、タイムアウト値を大きくすることは、使用するかどうかわからない接続を維持するためにメモリを消費する可能性があることを意味します。これにより、サーバーの同時接続ユーザーの許容量が減少し、最終的にはパフォーマンスを低下させる可能性があります。また、ほとんどの人は買い物をしたり、新しいおもちゃをカスタマイズしたりするのに5分以上かかるため、接続のタイムアウトに合わせてセッションのタイムアウト値を小さくすることは避けた方がよいでしょう。
重要な注意:HTTP/2はこれらの問題のいくつかに対処していますが、状態の維持に関連した他の問題も含んでいます。仕様では必須ではありませんが、主要なブラウザはHTTP/2 over TLS/SSLのみを許可しています。どちらのプロトコルも、再ネゴシエーションのパフォーマンス コストを回避しするためにパーシステンスを必要とし、そのためにはセッション認識、すなわちステートフルな動作を必要とします。
このようにして、関連付けられた接続が非アクティブになって終了した後も、サーバー上にメモリとしてセッションが残ることになり、貴重なリソースを大量に消費し、アプリケーションがうまく機能しないためにユーザーを怒らせてしまう可能性があります。
幸いなことに、この問題はCookieを使用することで解決されます。
Cookieとは、ブラウザがクライアントに保存するデータの断片です。Cookieは、ユーザーやそのアプリケーション、ユーザーがアクセスしたサイトに関するさまざまな興味深い情報をすべて保存することができます。「Cookie」という用語は、その考え方と名前の両方の元となった、UNIXコンピューティングのよく知られた概念である「マジック クッキー」に由来しています。Cookieは、HTTPヘッダーを介してブラウザとサーバーの間で作成され、共有されます。Cookieの例を以下に示します。
Cookie: JSESSIONID=9597856473431 Cache-Control: no-cache Host: 127.0.0.2:8080 Connection: Keep-Alive
ブラウザは自動的にHTTPヘッダーの中のCookieをコンピュータ上のファイルに保存すべきであることを認識し、ドメインごとにCookieを追跡します。ドメインのCookieは、常にブラウザによってHTTPヘッダーに含められてサーバーに渡されるため、Webアプリケーションの開発者はアプリケーションのサーバー側でその値を要求するだけで取得することができます。
セッションと接続の長さの問題は、Cookieによって解決されます。最新のWebアプリケーションのほぼすべてが「セッションID」を生成し、それをCookieとして渡します。これにより、アプリケーションは、セッションが作成された接続が終了した後でも、サーバー上でセッションを見つけることができます。このようにセッションIDを交換することで、HTTPのようなステートレスなプロトコルでも状態が維持されます。しかし、Webアプリケーションの使用が単一のWebサーバーやアプリケーション サーバーの能力を超えた場合はどうなるのでしょうか?通常は、ロード バランサ、または今日のアーキテクチャではアプリケーション デリバリ コントローラ(ADC)が、すべてのユーザーが可用性とパフォーマンスに満足できるようにアプリケーションを拡張するために導入されています。
最新のアプリケーションでは、Cookieを使用することもありますが、その他のHTTPヘッダーが重要性を増しています。認証と認可のためのAPIキーの多くはHTTPヘッダーを介して転送され、バックエンド サービスをルーティングしたり適切に拡張したりするために必要なデータも他のカスタム ヘッダーを介して転送されます。このデータを運ぶために従来の「Cookie」を使用するか、他のHTTPヘッダーを使用するかよりも、全体のアーキテクチャにとっての重要性を理解することの方が重要です。
ここでの問題点は、一般的にロード バランシング アルゴリズムではサーバー間での要求の分散のみが考慮されることです。ロード バランシング技術は、ラウンド ロビン、最小接続、最速応答時間などの業界標準のアルゴリズムに基づいています。これらはどれもステートフルではなく、同じユーザーがアプリケーションに対して行った各要求を別のサーバーに分散させることが可能です。これにより、あるサーバーのセッションに保存されたデータが「プール」内で他のサーバーと共有されることはほとんどないため、HTTP用に状態を実装するために行ったすべての取り組みが無駄になります。
そこで、パーシステンスの概念が役に立ちます。
パーシステンス(スティックネスとも呼ばれます)とはADCによって実装される技術であり、単一のユーザーからの要求が、常にそれらの要求が開始されたサーバーに分散されることを保証します。一部のロード バランシング製品やサービスでは、この技術を「スティッキー セッション」と表現していますが、この呼び名は非常に適切です。
計算量の多いネゴシエーション プロセスが完了してキーを交換した後、再度プロセスを開始するためのパフォーマンスは著しく低下するため、パーシステンスは長い間、SSL/TLS対応サイトのロード バランシングに使用されてきました。そのため、ユーザーが最初に接続したサーバーと同じサーバーに常に誘導されることを保証するために、ADCによってSSLセッション パーシステンスが実装されました。
長年にわたり、ブラウザの実装では、コストのかかるセッションの再ネゴシエーションを避けるための技術の開発を余儀なくされてきました。この技術はCookieベースのパーシステンスと呼ばれています。
ロード バランサは、SSL/TLSのセッションIDを使用するのではなく、Cookieを埋め込んで、クライアントが最初にサイトにアクセスしたときのセッションを一意に識別し、後続の要求でそのCookieを参照して、適切なサーバーへの接続を持続します。
Cookieベースのパーシステンスの概念はアプリケーション セッションに適用され、Webサーバーとアプリケーション サーバーによって生成されたセッションID情報を使用して、ユーザーの要求が同じセッション中に常に同じサーバーに誘導されることを保証します。この機能がなければ、ロード バランシングを必要とするアプリケーションは、セッション情報を共有する別の方法を見つけるか、またはセッションと接続のタイムアウト値を大きくしなければならず、ユーザー基盤をサポートするために必要なサーバーの数は手に負えないほどまで急速に膨れ上がるでしょう。
最も一般的な形のパーシステンスは、HTTPヘッダーで渡されるセッションIDを使用して実装されていますが、今日ではADCによってデータの他の部分でもパーシステンスが確保されています。Cookieに保存されるデータや、IP、TCP、HTTPヘッダーから派生したデータを、セッションを永続化するために使用することができます。実際、ユーザーを一意に識別するアプリケーション メッセージ内のデータをインテリジェントなADCが使用して、ブラウザとサーバーの間の接続を永続化することができます。
HTTPはステートレスなプロトコルかもしれませんが、私たちはユビキタス プロトコルに状態を強制的に適合させることに成功しました。パーシステンスとアプリケーション デリバリ コントローラを使用することで、状態を維持するために必要なCookieとセッションの多少不安定な統合を壊すことなく、高い可用性とパフォーマンスを備えたWebアプリケーションを構築することができます。
これらの機能により、HTTPの実装と実行はステートレスのまま、HTTPに状態を持たせることができます。Cookie、セッション、パーシステンスがなければ、私たちはおそらく、アプリケーションを構築するためのステートフルなプロトコルを探すことになったでしょう。代わりに、アプリケーション デリバリ コントローラが備える特長と機能が、ブラウザ(クライアント)とサーバーの間を仲介してこの機能を提供し、静的なWebページや従来のアプリケーションを超えて、最新のマイクロサービスベースのアーキテクチャやデジタル経済の申し子であるAPIにまで、HTTPの有用性を押し広げています。