ストリーミングエンドポイントの設計

Goaでストリーミングエンドポイントを設計する際は、一連の結果の送信を 処理できるメソッドを定義します。サーバーサイドストリーミング、 クライアントサイドストリーミング、または双方向ストリーミングを実装する 場合でも、GoaのDSLはこれらの動作を指定するための明確で簡潔な方法を 提供します。

StreamingResult DSLの使用

StreamingResult DSLは、メソッド定義内で使用され、メソッドが一連の 結果をクライアントにストリーミングすることを示します。これはResult DSL と相互に排他的であり、特定のメソッド内で使用できるのはいずれか一方のみです。

var _ = Service("logger", func() {
    Method("subscribe", func() {
        // LogEntryインスタンスがクライアントにストリーミングされます
        StreamingResult(LogEntry)
    })
})

この例では:

  • subscribeメソッド: LogEntryインスタンスを送信するストリーミングエンドポイントを定義します。
  • LogEntry: クライアントにストリーミングされる結果の型です。

ストリーミングメソッドを定義する際は、ストリーミングされるデータの型を 指定する必要があります。これは、結果の型をStreamingResult関数に渡すことで 行います。

制約と考慮事項

  • 相互排他性: メソッドはResultまたはStreamingResultのいずれかを使用でき、両方は使用できません。
  • 単一の結果型: ストリーミングされるすべての結果は同じ型のインスタンスである必要があります。
  • トランスポート独立性: 設計はトランスポートプロトコルに依存せず、Goaが適切なトランスポート固有のコードを生成できます。

StreamingPayload DSLの使用

StreamingPayload DSLは、メソッド定義内で使用され、メソッドが クライアントから一連のメッセージを受信することを示します。HTTPトランスポート で使用する場合、通常のPayload DSLと組み合わせて、初期接続パラメータと その後のストリーミングデータの両方を処理します。

var _ = Service("logger", func() {
    Method("subscribe", func() {
        // クライアントがLogEntryインスタンスをストリーミング
        StreamingPayload(LogEntry)

        // すべての更新の処理後に返される単一の結果
        Result(Summary)
    })
})

この例では:

  • subscribeメソッド: LogEntryインスタンスのストリームを受信するエンドポイントを定義します。
  • LogEntry: クライアントからストリーミングされるペイロードの型です。
  • Summary: すべての更新を処理した後に返される単一の結果です。

クライアントストリーミングメソッドを定義する際は、ストリームで受信する データの型を指定する必要があります。これは、ペイロード型をStreamingPayload 関数に渡すことで行います。

制約と考慮事項

  • 単一のペイロード型: ストリーミングされるすべてのペイロードは同じ型のインスタンスである必要があります。
  • トランスポート独立性: 設計はトランスポートプロトコルに依存せず、Goaが適切なトランスポート固有のコードを生成できます。
  • オプションの結果: クライアントストリーミングメソッドは、単一の結果を返すか、結果を返さないことがあります。
  • トランスポートの動作: HTTPでは、初期リクエストが処理され、接続がWebSocketにアップグレードされた後にストリームが確立されます。

通常のペイロードとの組み合わせ

HTTPエンドポイントでは、WebSocketアップグレード前の初期リクエストに 初期化パラメータを含めたい場合がよくあります。これは、StreamingPayload を通常のPayloadと組み合わせることで実現できます:

var _ = Service("logger", func() {
    Method("subscribe", func() {
        // 初期接続パラメータ
        Payload(func() {
            Field(1, "topic", String, "購読するログのトピック")
            Field(2, "api_key", String, "認証用のAPIキー")
            Required("topic", "api_key")
        })

        // クライアントからの更新ストリーム
        StreamingPayload(LogEntry)

        // すべての更新を処理した後の最終結果
        Result(Summary)

        HTTP(func() {
            GET("/logs/{topic}/stream")
            Param("api_key")
        })
    })
})

このパターンは以下の場合に特に有用です:

  • ストリームを確立する前の認証と承認
  • ストリーミングセッションのコンテキストまたはスコープの提供
  • ストリーム全体を通じて一定の初期パラメータの設定
  • WebSocket接続にアップグレードする前のリクエストの検証

HTTPトランスポートでは:

  1. 初期GETリクエストにtopicapi_keyパラメータが含まれます
  2. 検証後、接続がWebSocketにアップグレードされます
  3. クライアントがStreamingPayloadメッセージのストリーミングを開始します
  4. サーバーがストリームを処理し、最終結果を返すことがあります

まとめ

Goaでのストリーミングエンドポイントの設計は、StreamingResultStreamingPayload DSLを使用することで簡単です。サービスメソッド内で ストリーミングされるデータの型を定義することで、Goaの強力なコード生成を 活用して、基盤となるトランスポート固有のストリーミングロジックを処理 できます。これにより、ストリーミングエンドポイントの堅牢性、効率性、 保守性が確保されます。