This commit is contained in:
SATO Yusuke 2023-04-13 08:34:45 +08:00 committed by GitHub
commit 7eb95c0eba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 56 additions and 56 deletions

View File

@ -1328,21 +1328,21 @@ def set_user(user_id, values):
### Hypertext transfer protocol (HTTP)
HTTP はクライアントとサーバー間でのデータをエンコードして転送するための手法です。リクエスト・レスポンスに関わるプロトコルです。クライアントがリクエストをサーバーに投げ、サーバーがリクエストに関係するコンテンツと完了ステータス情報をレスポンスとして返します。HTTPは自己完結するので、間にロードバランサー、キャッシュ、エンクリプション、圧縮などのどんな中間ルーターが入っても動くようにできています。
HTTPはリクエスト・レスポンス型のプロトコルです。クライアントがリクエストを投げると、サーバーはそのリクエストに対応するコンテンツと完了ステータスをレスポンスとして返します。HTTPは自己完結したプロトコルです。リクエストやレスポンスが数多くのサーバーやルーターを通過し、その間にロードバランシング、キャッシュ、暗号化、圧縮などが行われていても、問題なく動くようにできています。
基本的なHTTPリクエストはHTTP動詞(メソッド)とリソース(エンドポイント)で成り立っています。以下がよくあるHTTP動詞です。:
基本的なHTTPリクエストはHTTPメソッド(動詞)とリソース(エンドポイント)で成り立っています。よく使われるHTTPメソッドとしては以下のものが挙げられます:
| 動詞 | 詳細 | 冪等性* | セーフ | キャッシュできるか |
| メソッド | 詳細 | 冪等性* | セーフ | キャッシュできるか |
|---|---|---|---|---|
| GET | リソースを読み取る | Yes | Yes | Yes |
| POST | リソースを作成するもしくはデータを処理するトリガー | No | No | Yes レスポンスが新しい情報を含む場合 |
| POST | リソースを作成する、またはデータを処理するプロセスをトリガーする | No | No | Yes レスポンスが新しい情報を含む場合 |
| PUT | リソースを作成もしくは入れ替える | Yes | No | No |
| PATCH | リソースを部分的に更新する | No | No | Yes レスポンスが新しい情報を含む場合 |
| DELETE | リソースを削除する | Yes | No | No |
*何度呼んでも同じ結果が返ってくること*
*何度呼んでも同じ結果が返ってくること
HTTPは**TCP** や **UDP** などの低級プロトコルに依存しているアプリケーションレイヤーのプロトコルである
HTTPはアプリケーション層のプロトコルで、**TCP** や **UDP** など下位のプロトコルの上で動作します
#### その他の参考資料、ページ: HTTP
@ -1358,21 +1358,21 @@ HTTPは**TCP** や **UDP** などの低級プロトコルに依存している
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>Source: How to make a multiplayer game</a></i>
</p>
TCPは[IP network](https://en.wikipedia.org/wiki/Internet_Protocol)の上で成り立つ接続プロトコルです。接続は[handshake](https://en.wikipedia.org/wiki/Handshaking)によって開始、解除されます。全ての送信されたパケットは欠損なしで送信先に送信された順番で到達するように以下の方法で保証されています:
TCPは[IP ネットワーク](https://en.wikipedia.org/wiki/Internet_Protocol)の上で動作する、コネクション指向のプロトコルです。コネクションの確立と切断は[ハンドシェイク](https://en.wikipedia.org/wiki/Handshaking)によって行われます。送出されたパケットはすべて、元々の順序通りに、かつデータ破損なしに宛先へ到達することが、以下の方法で保証されています:
* シーケンス番号と[checksum fields](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation)が全てのパケットに用意されている
* 各パケットにあるシーケンス番号と[チェックサム](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation)
* [Acknowledgement](https://en.wikipedia.org/wiki/Acknowledgement_(data_networks))パケットと自動再送信
もし送信者が正しいレスポンスを受け取らなかったとき、パケットを再送信します。複数のタイムアウトがあったとき、接続は解除されます。TCP は[フロー制御](https://en.wikipedia.org/wiki/Flow_control_(data)) と [輻輳制御](https://en.wikipedia.org/wiki/Network_congestion#Congestion_control)も実装しています。これらの機能によって速度は低下し、一般的にUDPよりも非効率な転送手段になっています。
送信側に正しいレスポンスが返ってこなかったら、パケットを再送信します。タイムアウトが何度も発生したときは、コネクションをdropします。TCP は[フロー制御](https://en.wikipedia.org/wiki/Flow_control_(data)) と [輻輳制御](https://en.wikipedia.org/wiki/Network_congestion#Congestion_control)も実装しています。これらの保証のために遅延が発生するので、一般的に転送効率はUDPより劣ります。
ハイスループットを実現するために、ウェブサーバーはかなり大きな数のTCP接続を開いておくことがあり、そのことでメモリー使用が圧迫されます。ウェブサーバスレッドと例えば[memcached](#memcached) サーバーの間で多数のコネクションを保っておくことは高くつくかもしれません。可能なところではUDPに切り替えるだけでなく[コネクションプーリング](https://en.wikipedia.org/wiki/Connection_pool)なども役立つかもしれません。
高スループットを確保するため、ウェブサーバーでは多数のTCPコネクションを開いたままにしておくことがあります。この場合は、メモリーの使用率が高くなります。ウェブサーバのスレッドと、例えば [memcached](#memcached) サーバーの間で多数のコネクションを張ったままにするのは高くつくかもしれません。この場合、可能であればUDPに切り替える、[コネクションプーリング](https://en.wikipedia.org/wiki/Connection_pool)を使うといった方法が役立つかもしれません。
TCPは高い依存性を要し、時間制約が厳しくないものに適しているでしょう。ウェブサーバー、データベース情報、SMTP、FTPやSSHなどの例に適用されます。
高い信頼性が必要な一方、応答時間の制約は厳しくないというアプリケーションには、TCPが適しています。例としてはウェブサーバー、データベース情報、SMTP、FTP、SSHなどが挙げられます。
以下の時にUDPよりもTCPを使うといいでしょう:
以下のような場合にはUDPよりもTCPを使うといいでしょう:
* 全てのデータが欠損することなしに届いてほしい
* ネットワークスループットの最適な自動推測をしてオペレーションしたい
* ネットワークスループットの利用効率を、予測上の最適値まで自動的に最適化したい
### ユーザデータグラムプロトコル (UDP)
@ -1382,13 +1382,13 @@ TCPは高い依存性を要し、時間制約が厳しくないものに適し
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>Source: How to make a multiplayer game</a></i>
</p>
UDPはコネクションレスです。データグラムパケットのようなものはデータグラムレベルでの保証しかされません。データグラムは順不同で受け取り先に到着したりそもそも着かなかったりします。UDPは輻輳制御をサポートしません。TCPにおいてはサポートされているこれらの保証がないため、UDPは一般的に、TCPよりも効率的です。
UDPはコネクションレスです。データグラムパケットのようなものはデータグラムレベルでの保証しかされません。データグラムは順不同で受け取り先に到着したりそもそも着かなかったりします。UDPは輻輳制御をサポートしません。TCPがサポートするこういった保証を行わないため、転送効率については一般的にTCPよりUDPの方が優れています。
UDPはサブネット上のすべての機器にデータグラムを送信することができます。これは[DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) において役に立ちます。というのも、クライアントはまだIPアドレスを取得していないので、IPアドレスを必要とするTCPによるストリームができないからです。
UDPブロードキャスト(サブネット上のすべての機器にデータグラムを送信することができます。これは[DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol)で役立ちます。というのも、クライアントはまだIPアドレスを取得していないので、IPアドレスを必要とするTCPではデータを流せないからです。
UDPは信頼性の面では劣りますが、VoIP、ビデオチャット、ストリーミングや同時通信マルチプレイヤーゲームなどのリアルタイム性が重視される時にはとても効果的です。
UDPは信頼性の面では劣りますが、VoIP、ビデオチャット、ストリーミング、リアルタイムマルチプレイヤーゲームなど、リアルタイム性が重視されるユースケースではとても効果的です。
TCPよりもUDPを使うのは:
以下のような場合にはTCPよりもUDPを使うといいでしょう:
* レイテンシーを最低限に抑えたい時
* データ欠損よりも、データ遅延を重視するとき
@ -1401,7 +1401,7 @@ TCPよりもUDPを使うのは:
* [TCP と UDPの違い](http://stackoverflow.com/questions/5970383/difference-between-tcp-and-udp)
* [Transmission control protocol](https://en.wikipedia.org/wiki/Transmission_Control_Protocol)
* [User datagram protocol](https://en.wikipedia.org/wiki/User_Datagram_Protocol)
* [Facebookメムキャッシュスケーリング](http://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf)
* [Facebookにおけるmemcacheのスケーリング](http://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf)
### 遠隔手続呼出 (RPC)
@ -1411,18 +1411,18 @@ TCPよりもUDPを使うのは:
<i><a href=http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview>Source: Crack the system design interview</a></i>
</p>
RPCではクライアントがリモートサーバーなどの異なるアドレス空間でプロシージャーが処理されるようにします。プロシージャーはローカルでのコールのように、クライアントからサーバーにどのように通信するかという詳細を省いた状態でコードが書かれます。リモートのコールは普通、ローカルのコールよりも遅く、信頼性に欠けるため、RPCコールをローカルコールと区別させておくことが好ましいでしょう。人気のRPCフレームワークは以下です。[Protobuf](https://developers.google.com/protocol-buffers/)、 [Thrift](https://thrift.apache.org/)、[Avro](https://avro.apache.org/docs/current/)
RPCでは、クライアントから別のアドレス空間通常はリモートサーバー上で実行される手続きを呼び出します。この処理はローカルでの手続き呼び出しと同じようにコーディングされます。クライアントプログラムがサーバーとどのように通信するかといった詳細は、抽象化により省略されます。リモート呼び出しは普通、ローカルでの呼び出しよりも遅く、信頼性に欠けます。そのため、RPC呼び出しとローカルの手続き呼び出しとは区別がつくようにしておいた方がいいでしょう。よく使われるRPCフレームワークとしては[Protobuf](https://developers.google.com/protocol-buffers/)、 [Thrift](https://thrift.apache.org/)、[Avro](https://avro.apache.org/docs/current/) などがあります。
RPC リクエストレスポンスプロトコル:
RPCはリクエストレスポンス型のプロトコルで、以下のように動作します:
* **クライアントプログラム** - クライアントスタブプロシージャーを呼び出します。パラメータはローカルでのプロシージャーコールのようにスタックへとプッシュされていきます。
* **クライアントスタブプロシージャー** - プロシージャIDとアーギュメントをパックしてリクエストメッセージにします。
* **クライアント通信モジュール** - OSがクライアントからサーバーへとメッセージを送ります。
* **サーバー通信モジュール** - OSが受け取ったパケットをサーバースタブプロシージャーに受け渡します。
* **サーバースタブプロシージャー** - 結果を展開し、プロシージャーIDにマッチするサーバープロシージャーを呼び出し、結果を返します。
* サーバーレスポンスは上記のステップを逆順で繰り返します。
* **クライアントプログラム** - クライアントプログラムが、クライアントスタブプロシージャーを呼び出します。パラメータはローカルでの手続き呼び出しのようにスタックへとプッシュされます。
* **クライアントスタブプロシージャー** - クライアントスタブプロシージャーが、プロシージャIDと引数をマーシャライズパックしてリクエストメッセージにします。
* **クライアント通信モジュール** - クライアント通信モジュールで、OSがクライアントからサーバーへとメッセージを送ります。
* **サーバー通信モジュール** - サーバー通信モジュールで、OSが受け取ったパケットをサーバースタブプロシージャーに受け渡します。
* **サーバースタブプロシージャー** - サーバースタブプロシージャーが、受信した内容を展開し、プロシージャーIDにマッチするサーバープロシージャーを呼び出し、与えられた引数を渡します。
* サーバーがレスポンスを返す際は、上記のステップを逆順で行います。
Sample RPC calls:
RPC呼び出しの例:
```
GET /someoperation?data=anId
@ -1434,36 +1434,36 @@ POST /anotheroperation
}
```
RPCは振る舞いを公開することに焦点を当てています。RPCは内部通信パフォーマンスを理由として使われることが多いです。というのも、使用する状況に合わせてネイティブコールを自作することができるからです。
RPCは振る舞いを公開することに焦点を当てています。RPCはそのパフォーマンスを理由に、内部通信においてよく利用されます。場合によっては、ユースケースに合わせてネイティブコールを自作することもできます。
ネイティブライブラリー (aka SDK) を呼ぶのは以下の時:
次のような場合はネイティブライブラリー (SDKとも呼ぶ) を使うほうがいいでしょう:
* ターゲットのプラットフォームを知っている時
* ロジックがどのようにアクセスされるのかを管理したいとき
* ライブラリー外でエラーがどのようにコントロールされるかを管理したい時
* パフォーマンスとエンドユーザーエクスペリエンスが最優先の
* ターゲットのプラットフォームが分かっているとき
* 「ロジックに」対するアクセス方法を制御したいとき
* ライブラリーでエラーがどのようにコントロールされるかをを制御したいとき
* パフォーマンスとエンドユーザーエクスペリエンスが最優先のとき
**REST** プロトコルに従うHTTP APIはパブリックAPIにおいてよく用いられます。
公開APIにおいては、**REST** に基づくHTTP APIの方がよく用いられています。
#### 欠点: RPC
* RPCクライアントとはサービス実装により厳密に左右されることになります。
* 新しいオペレーション、使用例があるたびに新しくAPIが定義されなければなりません。
* RPCクライアントがサービスの実装に密結合してしまいます。
* 新しい操作やユースケースができるたびに、新しいAPIを定義しなければなりません。
* RPCをデバッグするのは難しい可能性があります。
* 既存のテクノロジーをそのまま使ってサービスを構築することはできないかもしれません。例えば、[Squid](http://www.squid-cache.org/)などのサーバーに[RPCコールが正しくキャッシュ](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/) されるように追加で骨を折る必要があるかもしれません。
* 既存のテクノロジーをそのまま活用することはできないかもしれません。例えば、[Squid](http://www.squid-cache.org/) などのサーバーに[RPCコールを正しくキャッシュさせる](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/) には追加の作業が必要になるかもしれません。
### Representational state transfer (REST)
RESTは、クライアントがサーバーによってマネージされるリソースに対して処理を行うクライアント・サーバーモデルを支持するアーキテキチャスタイルです。サーバーは操作できるもしくは新しいリソースレプレゼンテーションを受け取ることができるようなリソースやアクションのレプレゼンテーションを提供します。すべての通信はステートレスでキャッシュ可能でなければなりません。
RESTはアーキテクチャスタイルの一つで、クライアント・サーバーモデルを推し進めたものです。クライアントは、サーバーが管理しているリソースに対して処理を行います。サーバーはリソースやアクションに対する「表現」(representation) を提供し、クライアントはそれに対する操作を行ったり、新しいリソースの表現を受け取ったりできます。すべての通信はステートレスでキャッシュ可能でなければなりません。
RESTful なインターフェースには次の四つの特徴があります:
* **特徴的なリソース (URI in HTTP)** - どのオペレーションであっても同じURIを使う。
* **HTTP動詞によって変わる (Verbs in HTTP)** - 動詞、ヘッダー、ボディを使う
* **自己説明的なエラーメッセージ (status response in HTTP)** - ステータスコードを使い、新しく作ったりしないこと。
* **[HATEOAS](http://restcookbook.com/Basics/hateoas/) (HTML interface for HTTP)** - 自分のwebサービスがブラウザで完全にアクセスできること。
* **リソースの一意性(HTTPにおけるURI)** - どのオペレーションであっても同じURIを使うこと
* **表現(HTTPにおけるメソッド)によって変化する** - メソッド、ヘッダー、ボディを使うこと。
* **自己説明的なエラーメッセージ (HTTPにおけるステータスレスポンス)** - ステータスコードを使うこと。車輪の再発明はしないこと。
* **[HATEOAS](http://restcookbook.com/Basics/hateoas/) (HTTPにおけるHTMLインタフェース)** - Webサービスがブラウザから完全にアクセスできること。
サンプル REST コール:
REST呼び出しの例:
```
GET /someresources/anId
@ -1472,26 +1472,26 @@ PUT /someresources/anId
{"anotherdata": "another value"}
```
RESTはデータを公開することに焦点を当てています。クライアントとサーバーのカップリングを最小限にするもので、パブリックAPIなどによく用いられます。RESTはURI、 [representation through headers](https://github.com/for-GET/know-your-http-well/blob/master/headers.md)、そして、GET、POST、PUT、 DELETE、PATCHなどのHTTP動詞等のよりジェネリックで統一されたメソッドを用います。ステートレスであるのでRESTは水平スケーリングやパーティショニングに最適です。
RESTはデータを公開することに焦点を当てています。RESTではクライアントとサーバーの結合度を最小にでき、公開APIなどによく用いられます。RESTでは、リソースの公開にはURI、リソースの[表現にはHTTPヘッダー](https://github.com/for-GET/know-your-http-well/blob/master/headers.md)、アクションにはHTTPメソッドGET, POST, PUT, DELETE, PATCHといったように、そのそれぞれに対してより一般的で統一された方法を用います。RESTはステートレスなので、水平スケーリングやパーティショニングに最適です。
#### 欠点: REST
* RESTはデータ公開に焦点を当てているので、リソースが自然に整理されていなかったり、シンプルなヒエラルキーで表せられない時にはよい選択肢とは言えないかもしれません。例えば、とあるイベントのセットにマッチするすべての更新情報を返すと言った処理は簡単にはパスで表現することができません。RESTでは、URIパス、クエリパラメータ、そして場合によってはリクエストボディなどによって実装されることが多いでしょう。
* RESTは少数の動詞に依存しています(GET、POST、PUT、DELETE、そして PATCH) が時には使いたい事例に合わないことがあります。例えば、期限の切れたドキュメントをアーカイブに移したい場合などはこれらの動詞の中には綺麗にはフィットしません。
* ネストされたヒエラルキーの中にあるリソースをとってくるのはシングルビューを描画するのにクライアントとサーバー間で数回やりとりしなければなりません。例として、ブログエントリーのコンテンツとそれに対するコメントを表示する場合などです。様々なネットワーク環境で動作する可能性が考えられるモバイルアプリケーションにおいてはこのような複数のやり取りは好ましくありません。
* 時が経つにつれて、APIレスポンスにより多くのフィールドが与えられて、古いクライアントはすでにいらないものも含めてすべてのデータフィールドを受け取ることになります。そのことで、ペイロードが大きくなりすぎて、レイテンシーも拡大することになります。
* RESTはデータ公開に焦点を当てているので、リソースが自然に整理されていなかったり、シンプルな階層構造で表せない時にはよい選択肢とは言えないかもしれません。例えば、ある複数のイベントで直近1時間に更新されたすべてのレコードを返すといった処理を、パスとして表現するのは簡単ではありません。RESTではこのような場合、URIパス、クエリパラメータ、そして場合によってはリクエストボディなどの組み合わせで実装することになるでしょう。
* 一般的に、RESTでは限られたHTTPメソッドのみ(GET、POST、PUT、DELETE、PATCH)を使いますが、これがユースケースに合わないことがあります。例えば、期限の切れたドキュメントをアーカイブに移したい場合などはこれらのメソッドには綺麗にはフィットしません。
* 入れ子になった階層構造を持つ、複雑なリソースを取得する場合、一つのビューを描画するだけでもクライアントとサーバーの間で複数回のやりとりが発生します。例えば、ブログのエントリーの内容と、それに対するコメントを取得する場合が該当します。ネットワークが変化する環境で動くモバイルアプリケーションにおいてはこのようなやり取りが複数回発生するの非常に好ましくありません。
* 時が経つにつれて、APIレスポンスにはより多くのフィールドが追加されていきます。この際、古くからあるクライアントは、追加されたフィールドを使いもしないのにすべて受け取ることになります。結果として、ペイロードも膨れますし、レイテンシーも拡大することになります。
### RPCとREST比較
### RPCとREST比較
| Operation | RPC | REST |
| 操作 | RPC | REST |
|---|---|---|
| サインアップ | **POST** /signup | **POST** /persons |
| リザイン | **POST** /resign<br/>{<br/>"personid": "1234"<br/>} | **DELETE** /persons/1234 |
| Person読み込み | **GET** /readPerson?personid=1234 | **GET** /persons/1234 |
| Personのアイテムリスト読み込み | **GET** /readUsersItemsList?personid=1234 | **GET** /persons/1234/items |
| Personのアイテムへのアイテム追加 | **POST** /addItemToUsersItemsList<br/>{<br/>"personid": "1234";<br/>"itemid": "456"<br/>} | **POST** /persons/1234/items<br/>{<br/>"itemid": "456"<br/>} |
| アイテム更新 | **POST** /modifyItem<br/>{<br/>"itemid": "456";<br/>"key": "value"<br/>} | **PUT** /items/456<br/>{<br/>"key": "value"<br/>} |
| アイテム削除 | **POST** /removeItem<br/>{<br/>"itemid": "456"<br/>} | **DELETE** /items/456 |
| 退会 | **POST** /resign<br/>{<br/>"personid": "1234"<br/>} | **DELETE** /persons/1234 |
| 個人データの読み込み | **GET** /readPerson?personid=1234 | **GET** /persons/1234 |
| 個人のアイテムリスト読み込み | **GET** /readUsersItemsList?personid=1234 | **GET** /persons/1234/items |
| 個人のアイテムリストへアイテムを追加 | **POST** /addItemToUsersItemsList<br/>{<br/>"personid": "1234";<br/>"itemid": "456"<br/>} | **POST** /persons/1234/items<br/>{<br/>"itemid": "456"<br/>} |
| アイテム更新 | **POST** /modifyItem<br/>{<br/>"itemid": "456";<br/>"key": "value"<br/>} | **PUT** /items/456<br/>{<br/>"key": "value"<br/>} |
| アイテム削除 | **POST** /removeItem<br/>{<br/>"itemid": "456"<br/>} | **DELETE** /items/456 |
<p align="center">
<i><a href=https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/>Source: Do you really know why you prefer REST over RPC</a></i>