# Grid アーキテクチャ

URL: https://deploy-preview-2977--selenium-dev.netlify.app/ja/documentation/grid/architecture/

---

Grid は、Grid を運用するためのコンポーネントの集合体として設計されています。
非常に複雑に見えるかもしれませんが、
このドキュメントが混乱を解消する手助けとなることを期待します。

## キーとなるコンポーネント

主要な Grid のコンポーネント:

<dl>
<dt>イベントバス
<dd>他のコンポーネント間で非同期で受信される可能性のあるメッセージを送信します。

<dt>新規セッションキュー
<dd>まだディストリビューターによってノードに割り当てられていない
    受信セッションの一覧を管理します。

<dt>ディストリビューター
<dd>Grid内で<a
    href="https://w3c.github.io/webdriver/#dfn-sessions">WebDriverセッション
    </a>を実行する場所（"スロット"と呼ぶ）を管理し、
    新しいセッションリクエストを受け取りスロットに割り当てる役割を担います。

<dt>ノード
<dd><a href="https://w3c.github.io/webdriver/#dfn-sessions">WebDriverセッション
    </a>を実行します。各セッションはスロットに割り当てられ、
    各ノードは1つ以上のスロットを持っています。

<dt>セッションマップ
<dd><a href="https://w3c.github.io/webdriver/#dfn-session-id">セッションID</a>
    とセッションが実行されているノードのアドレスのマップを管理します。

<dt>ルーター
<dd>Gridのフロントエンドとして動作します。
    Gridの中で唯一ウェブに公開してもよい部分です（ただし、公開しないことを強く推奨します）。
    受け取ったリクエストを新規セッションキューかセッションが実行されているノードどちらかに振り分けます。
</dl>

グリッドについて説明する際に、覚えておくと便利な概念がいくつかあります:

- **スロット** はセッションが実行されるところです。
- 各スロットは **ステレオタイプ** を持っています。これは
  ディストリビューターがスロットを所有するノードに
  [新規セッション][] リクエストを送信する前に、
  リクエストがマッチしなければならない最小限の capabilities のセットです。
- **Grid モデル** はディストリビューターが Grid の状態を追跡する方法です。
  その名が示すように、これは時々実際と一致しないことがあります
  （ディストリビューターが開始したばかりだからかもしれません）。
  ディストリビューターが新規セッションのリクエストを素早くスロットに割り当てることができるように、
  各ノードに問い合わせるより優先して利用されます。

## 同期呼び出しと非同期呼び出し

Grid 内では主に 2 つの通信メカニズムが使われています。

1.  同期で "REST 風" な、JSON を用いた HTTP リクエスト
2.  イベントバスによって送信される非同期なイベント

どちらの仕組みを使うべきかどのように決めればよいでしょうか？
結局のところ、Grid 全体をイベントベースにモデリングしたとしても、
うまくいくでしょう。

答えは、もし実行されるアクションが同期である場合(例えばほとんどの WebDriver の呼び出しなど)、
あるいはレスポンスを受け取れなかったときに問題になるような場合、
Grid は同期呼び出しを利用します。かわりにもし、関心のあるコンポーネントに情報を
ブロードキャストしたい場合、あるいはレスポンスを受け取れなくても問題にならない場合は
イベントバスを使用することが望ましいです。

特筆する点として、非同期呼び出しは同期呼び出しよりも、
よりリスナーが分離されています。

## 起動シーケンスとコンポーネント間の依存

Grid はコンポーネントを任意の順番で起動できるように設計されていますが、
概念的にはコンポーネントが起動する順番は:

1. イベントバスとセッションマップが最初に起動します。これらは
   他のコンポーネントへの依存もお互いの依存もないため、
   並列に開始しても安全です。
2. 次に 新規セッションキューが起動します。
3. ここでディストリビューターが起動できるようになります。これは
   定期的に 新規セッションキューに接続し、ジョブをポーリングします。
   このポーリングはイベント（新規セッションがキューに追加された）あるいは
   定期的な間隔によって行われる可能性があります。
4. ルーターを起動できます。新しいセッションのリクエストは
   新規セッションキューに送られ、ディストリビューターはセッションを実行する
   スロットを探そうとします。
5. これでノードを起動することができます。ノードが Grid に登録されるまでの
   流れは後述します。登録が完了すると、Grid はトラフィックを提供することが
   できるようになります。

コンポーネント間の依存関係を以下のよう表すことができます。
"✅" は同期的な依存関係があることを示しています。

|                      | イベントバス | ディストリビューター | ノード | ルーター | セッションマップ | 新規セッションキュー |
| -------------------- | ------------ | -------------------- | ------ | -------- | ---------------- | -------------------- |
| イベントバス         | X            |                      |        |          |                  |                      |
| ディストリビューター | ✅           | X                    | ✅     |          |                  | ✅                   |
| ノード               | ✅           |                      | X      |          |                  |                      |
| ルーター             |              |                      | ✅     | X        | ✅               |                      |
| セッションマップ     |              |                      |        |          | X                |                      |
| 新規セッションキュー | ✅           |                      |        |          |                  | X                    |

## ノードの登録

Grid に新しいノードを登録するプロセスは軽量です。

1. ノードは開始する時 "ハートビート" イベントを発信する必要があります。
   このハートビートは[Node Status]を含んでいます.
1. ディストリビューターはハートビートイベントを受け取ります。
   受け取ったら、ノードの `/status` エンドポイントに `GET` リクエストを試みます。
   この情報をもとに Grid が設定されます。

ディストリビューターは同じ `/status` エンドポイントを使用して、定期的にノードをチェックしますが、
ノードは起動した後もハートビートを送り続けなければなりません。
これにより、ディストリビューターは Grid の状態を永続化しませんが、
再起動しても Grid を（最終的に）最新の状態にすることができます。

### Node Status オブジェクト

Node Status は以下のフィールドを持つ JSON オブジェクトです:

| 名前               | 型      | 概要                                                                                                                                                                                                   |
| ------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| availability       | string  | `up`, `draining`, `down` のいずれかの文字列です。重要なのは `draining` で、これはノードに新しいセッションを送らないことを示し、最後のセッションが終了するとノードは終了、または再起動します。          |
| externalUrl        | string  | Grid 内の他のコンポーネントが接続するための URI。                                                                                                                                                      |
| lastSessionCreated | integer | このノードで最後にセッションが作成された時間のエポックタイムスタンプです。ディストリビューターは、他の条件がすべて同じであれば、最も長くアイドルであったノードに新しいセッションを送信しようとします。 |
| maxSessionCount    | integer | セッション数は利用可能なスロット数をカウントすることで推測できますが、この値はノードが「満杯」とみなされるまでに、ノード上で同時に実行されるセッションの最大数を決定するために使用されます。           |
| nodeId             | string  | ノード ID を識別する UUID です。                                                                                                                                                                       |
| osInfo             | object  | `arch`, `name`, `version` フィールドを持つオブジェクトです。Grid UI と GraphQL クエリーで利用されます。                                                                                                |
| slots              | array   | Slot オブジェクト(以下を参照)の配列。                                                                                                                                                                  |
| version            | string  | ノードのバージョン(Selenium では、これは Selenium のバージョンと同じです)。                                                                                                                            |

すべてのフィールドの値を設定することが推奨されます。

### Slot オブジェクト

Slot オブジェクトは 1 ノードでの 1 スロットを表します。
1 スロットにつき 1 セッションを実行することができます。
1 つのノードが同時に実行できる数よりも多くのスロットを持つことができます。
例えばあるノードが 10 セッションまで同時に実行でき、
それらのセッションは Chrome, Edge, Firefox のどの組み合わせでも良い時、
ノードは 'max session count' を 10 として、
10 の Chrome スロット、10 の Edge スロット、10 の Firefox スロットを持ちます。

| 名前        | 型     | 概要                                                                                                           |
| ----------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| id          | string | スロット ID。                                                                                                  |
| lastStarted | string | スロットが最後にセッションを開始した時間。ISO-8601 フォーマット。                                              |
| stereotype  | object | このスロットがマッチする最小限の[capabilities][capabilities] のセット。 最小の例: `{"browserName": "firefox"}` |
| session     | object | Session オブジェクト(以下を参照)。                                                                             |

### Session オブジェクト

スロットで実行中のセッションを表します。

| 名前         | 型     | 概要                                                                                                                                |
| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| capabilities | object | セッションが持つ実際の capabilities。 The actual capabilities provided by the session. [新規セッション]コマンドの戻り値と同じです。 |
| startTime    | string | セッションが開始した時間。ISO-8601 フォーマット。                                                                                   |
| stereotype   | object | このスロットがマッチする最小限の[capabilities][capabilities] のセット。 最小の例: `{"browserName": "firefox"}`                      |
| uri          | string | ノードがセッションに接続するための URI。                                                                                            |

[capabilities]: https://w3c.github.io/webdriver/#dfn-merging-capabilities
[新規セッション]: https://w3c.github.io/webdriver/#new-session
[node status]: https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/grid/data/NodeStatus.html
[session]: https://w3c.github.io/webdriver/#dfn-sessions
[session id]: https://w3c.github.io/webdriver/#dfn-session-id
