> Source: https://www.ymotongpoo.com/works/oteps/otep-0099/


# OTEP-0099: OTLP/HTTP: OTLPのためのHTTPトランスポート拡張

これは[OTLP](0035-opentelemetry-protocol.md)（OpenTelemetry Protocol）にHTTPトランスポート拡張を追加する提案です。

## 目次 {#table-of-contents}

* [動機](#motivation)
* [OTLP/HTTPプロトコルの詳細](#otlphttp-protocol-details)
  * [リクエスト](#request)
  * [レスポンス](#response)
    * [成功](#success)
    * [失敗](#failures)
    * [スロットリング](#throttling)
    * [その他すべてのレスポンス](#all-other-responses)
  * [コネクション](#connection)
  * [並列コネクション](#parallel-connections)
* [先行技術と代替技術](#prior-art-and-alternatives)

## 動機 {#motivation}

OTLPは現在、gRPCという1つのトランスポートを介してのみ通信できます。
gRPCを使うことには一定のメリットがありますが、欠点もあります。

- 一部のユーザーはインフラストラクチャーの制約により、gRPCベースのプロトコルを利用できません。
  たとえば、AWS ALBはgRPCコネクションをサポートしていません。

- gRPCは比較的大きな依存関係であり、それを受け入れたくないクライアントもいます。
  プレーンなHTTPはより小さな依存関係であり、多くのプログラミング言語の標準ライブラリに組み込まれています。

## OTLP/HTTPプロトコルの詳細 {#otlphttp-protocol-details}

この提案は、OTLP over gRPCトランスポート（以下OTLP/gRPC）の既存の仕様を維持しつつ、OTLPプロトコルをHTTPトランスポート上で使用する追加の方法（以下OTLP/HTTP）を定義します。
OTLP/HTTPは、OTLP/gRPCが使用するのと同じprotobufペイロードを使用し、このペイロードがHTTPトランスポート上でどのように通信されるかを定義します。

OTLP/HTTPは、クライアントからサーバーへテレメトリーデータを送信するためにHTTP POSTリクエストを使用します。
実装はHTTP/1.1またはHTTP/2トランスポートを使用してもよい(MAY)です。
HTTP/2トランスポートを使用する実装は、HTTP/2コネクションを確立できない場合、HTTP/1.1トランスポートにフォールバックすべき(SHOULD)です。

### リクエスト {#request}

テレメトリーデータはHTTP POSTリクエストによって送信されます。

トレースデータを運ぶリクエストのデフォルトのURLパスは `/v1/traces` です（たとえば、"example.com" サーバーに接続する場合の完全なURLは `https://example.com/v1/traces` になります）。
リクエストボディは、ProtoBufでエンコードされた[`ExportTraceServiceRequest`](https://github.com/open-telemetry/opentelemetry-proto/blob/e6c3c4a74d57f870a0d781bada02cb2b2c497d14/opentelemetry/proto/collector/trace/v1/trace_service.proto#L38)メッセージです。

メトリクスデータを運ぶリクエストのデフォルトのURLパスは `/v1/metrics` であり、リクエストボディは、ProtoBufでエンコードされた[`ExportMetricsServiceRequest`](https://github.com/open-telemetry/opentelemetry-proto/blob/e6c3c4a74d57f870a0d781bada02cb2b2c497d14/opentelemetry/proto/collector/metrics/v1/metrics_service.proto#L35)メッセージです。

クライアントは "Content-Type: application/x-protobuf" リクエストヘッダーを設定しなければなりません(MUST)。
クライアントはコンテンツをgzip圧縮してもよく(MAY)、その場合は "Content-Encoding: gzip" リクエストヘッダーを含めるべきです(SHOULD)。
クライアントは、gzipでエンコードされたレスポンスを受信できる場合、 "Accept-Encoding: gzip" リクエストヘッダーを含めてもよい(MAY)です。

リクエストのデフォルト以外のURLパスは、クライアント側とサーバー側の両方で設定できてもよい(MAY)です。

### レスポンス {#response}

#### 成功 {#success}

成功時には、サーバーは `HTTP 200 OK` で応答しなければなりません(MUST)。
レスポンスボディは、トレースの場合はProtoBufでエンコードされた[`ExportTraceServiceResponse`](https://github.com/open-telemetry/opentelemetry-proto/blob/e6c3c4a74d57f870a0d781bada02cb2b2c497d14/opentelemetry/proto/collector/trace/v1/trace_service.proto#L47)メッセージ、メトリクスの場合は[`ExportMetricsServiceResponse`](https://github.com/open-telemetry/opentelemetry-proto/blob/e6c3c4a74d57f870a0d781bada02cb2b2c497d14/opentelemetry/proto/collector/metrics/v1/metrics_service.proto#L44)メッセージでなければなりません(MUST)。

サーバーは "Content-Type: application/x-protobuf" レスポンスヘッダーを設定しなければなりません(MUST)。
リクエストヘッダーに "Accept-Encoding: gzip" が存在する場合、サーバーはレスポンスをgzipエンコードし、 "Content-Encoding: gzip" レスポンスヘッダーを設定してもよい(MAY)です。

サーバーは、リクエストのデコードと検証に成功した後より前には、成功で応答すべきではありません(SHOULD)。

#### 失敗 {#failures}

リクエストの処理が失敗した場合、サーバーは適切な `HTTP 4xx` または `HTTP 5xx` ステータスコードで応答しなければなりません(MUST)。
使用すべき具体的な失敗ケースとHTTPステータスコードの詳細については、以下のセクションを参照してください。

すべての `HTTP 4xx` および `HTTP 5xx` レスポンスのレスポンスボディは、問題を説明する、ProtoBufでエンコードされた[Status](https://pkg.go.dev/google.golang.org/genproto/googleapis/rpc/status#Status)メッセージでなければなりません(MUST)。

この仕様は `Status.code` フィールドを使用せず、サーバーは `Status.code` フィールドを省略してもよい(MAY)です。
クライアントは `Status.code` フィールドに基づいて振る舞いを変更することは想定されていませんが、トラブルシューティングの目的でそれを記録してもよい(MAY)です。

`Status.message` フィールドには、 `Status` メッセージのスキーマで定義されているような、開発者向けのエラーメッセージを含めるべきです(SHOULD)。

サーバーは `Status.details` フィールドに追加の詳細を含めてもよい(MAY)です。
このフィールドが個々の失敗ケースで何を含み得るかについては、以下を参照してください。

#### 不正なデータ {#bad-data}

リクエストの処理が、リクエストにデコードできない、あるいは他の理由で無効なデータが含まれていることが原因で失敗し、その失敗が永続的なものである場合、サーバーは `HTTP 400 Bad Request` で応答しなければなりません(MUST)。
レスポンス中の `Status.details` フィールドには、不正なデータを説明する[BadRequest](https://github.com/googleapis/googleapis/blob/d14bf59a446c14ef16e9931ebfc8e63ab549bf07/google/rpc/error_details.proto#L166)を含めるべきです(SHOULD)。

クライアントは `HTTP 400 Bad Request` レスポンスを受信した場合、リクエストを再試行してはなりません(MUST NOT)。

#### スロットリング {#throttling}

サーバーが、クライアントに許可されている数を超えるリクエストを受信した場合、あるいはサーバーが過負荷状態にある場合、サーバーは `HTTP 429 Too Many Requests` または `HTTP 503 Service Unavailable` で応答すべきであり(SHOULD)、再試行までに待機すべき推奨時間間隔を秒数で示す[「Retry-After」](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3)ヘッダーを含めてもよい(MAY)です。

クライアントは、 "Retry-After" ヘッダーが存在する場合、そこで指定された待機間隔を尊重すべきです(SHOULD)。
クライアントが `HTTP 429` または `HTTP 503` レスポンスを受信し、レスポンスに "Retry-After" ヘッダーが存在しない場合、クライアントは再試行の間に指数バックオフ戦略を実装すべきです(SHOULD)。

#### その他すべてのレスポンス {#all-other-responses}

このドキュメントに明示的に記載されていないその他すべてのHTTPレスポンスは、HTTP仕様に従って扱われるべきです。

サーバーがレスポンスを返さずに切断した場合、クライアントは再試行して同じリクエストを送信すべきです(SHOULD)。
クライアントは、サーバーに過負荷をかけないように、再試行の間に指数バックオフ戦略を実装すべきです(SHOULD)。

### コネクション {#connection}

クライアントがサーバーに接続できない場合、クライアントは再試行の間に指数バックオフ戦略を使用してコネクションを再試行すべきです(SHOULD)。
再試行間の間隔にはランダムなジッターを持たせなければなりません。

クライアントはリクエスト間でコネクションを維持すべきです(SHOULD)。

サーバー実装は、OTLP/gRPCとOTLP/HTTPのリクエストを同じポートで処理し、 "Content-Type" リクエストヘッダーに基づいて対応するトランスポートハンドラーにコネクションを多重化してもよい(MAY)です。

### 並列コネクション {#parallel-connections}

より高い総スループットを達成するために、クライアントは複数の並列HTTPコネクションを使用してリクエストを送信してもよい(MAY)です。
その場合、並列コネクションの最大数は設定可能であるべきです(SHOULD)。

## 先行技術と代替技術 {#prior-art-and-alternatives}

私はHTTP/1.1+WebSocketトランスポートについても検討しました。
OTLP over WebSocketトランスポートの実験的な実装では、通常、プレーンなHTTPトランスポート実装よりも優れたパフォーマンスを示すことが分かりました（WebSocketはCPU使用率が低く、高レイテンシーのコネクションでスループットが高い）。
しかし、WebSocketトランスポートの実装はやや複雑になり、WebSocketライブラリはプレーンなHTTPほど普及していないため、特定の言語では実装が困難、あるいは不可能になる可能性があります。

HTTP/1.1+WebSocketトランスポートは、OTLP/gRPCおよびOTLP/HTTPよりも優れたパフォーマンスを示すため、高パフォーマンスなユースケース向けの将来のトランスポートとして検討される可能性があります。

