11

HTTP が要求応答プロトコルであることは知っています。私の問題は、要するに、クライアントがサーバーに長時間実行プロセスを開始するように要求し、進行状況情報を含む単純な JSON メッセージで進行状況をクライアントに通知したいということです。

HTTP/1.1 では、WebSocket またはサーバー送信イベント (SSE) またはロング ポーリングを使用できることを知っています。

HTTP/2 はまだ WebSocket をサポートしていないことがわかりました。

私の質問は、HTTP/2 でそのようなことを処理する最適な方法は何ですか?

HTTP/2 でサーバーが開始した要求を処理するために、私が認識していない新しいことはありますか?

それが問題なら、私はGo言語を使用しています。

4

3 に答える 3

9

Websocket の前には、ポーリングがありました。これは文字通り、クライアントが定期的に (数秒ごと、またはアプリケーションにとって意味のある期間)、サーバーにリクエストを送信してジョブのステータスを確認することを意味します。

多くの人が使用する最適化は、「長い」ポーリングです。これには、サーバーがリクエストを受け入れ、サーバーの内部で変更をチェックし、何もない間は特定のタイムアウトに達するか目的のイベントが発生するまでスリープし、クライアントにメッセージが返されるようにすることが含まれます。

タイムアウトに達すると、接続が閉じられ、クライアントは別の要求を行う必要があります。サーバーコードは次のようになります。関数が名前と署名に基づいて適切な処理を行うと仮定します。

import (
    "net/http"
    "time"
)

func PollingHandler(w http.ResponseWriter, r *http.Request) {
    jobID := getJobID(r)
    for finish := 60; finish > 0; finish-- { // iterate for ~1 minute
        status, err := checkStatus(jobID)
        if err != nil {
            writeError(w, err)
            return
        }
        if status != nil {
            writeStatus(w, status)
            return
        }
        time.Sleep(time.Second) // sleep 1 second
    }
    writeNil(w) // specific response telling client to request again.
}

タイムアウトを処理するためのより良い方法は、コンテキスト パッケージを使用し、タイムアウトのあるコンテキストを作成することです。それは次のようになります。

import (
    "net/http"
    "time"
    "golang.org/x/net/context"
)

func PollingHandler(w http.ResponseWriter, r *http.Request) {
    jobID := getJobID(r)
    ctx := context.WithTimeout(context.Background(), time.Second * 60)
    for {
        select{
        case <-ctx.Done():
            writeNil(w)
        default: 
            status, err := checkStatus(jobID)
            if err != nil {
                writeError(w, err)
                return
            }
            if status != nil {
                writeStatus(w, status)
                return
            }
            time.Sleep(time.Second) // sleep 1 second
        }
    }

}

checkStatusこの 2 番目のバージョンは、特に呼び出しが遅くなる可能性がある場合に、より信頼性の高い時間で返されます。

于 2015-06-15T04:33:13.317 に答える
1

JSON メッセージをテキストとして送信する場合は、サーバー送信イベント (SSE)が適しています。SSE は、テキストを送信するように設計されています。すべてのイベント データは UTF-8 文字でエンコードされます。欠点は、SSE を介してバイナリ データを送信することが非効率になることです。

バイナリ データを送信する場合は、HTTP/2 によって導入されたサーバー プッシュメカニズムに興味があるかもしれません。サーバー プッシュを使用すると、HTTP/2 サーバーは任意の種類のファイルを独自の主導でクライアントに送信できます。これは、クライアントが要求する前に送信されますが、サーバー プッシュの「応答」と呼ばれます。クライアントは、サーバー プッシュ応答を介して送信されたファイルを自動的にキャッシュに保存します。ファイルに対する後続の要求は、サーバーへのラウンドトリップなしでキャッシュからすぐに実行されます。

これは、バイナリ データを Web ブラウザにプッシュする効率的な方法です。問題は、サーバー プッシュ応答が到着したときに、ブラウザーのドキュメント オブジェクト モデル (DOM) に通知されないことです。ブラウザは、リクエストを行ったときにのみ、データがキャッシュにあることを検出します。この問題は、次の方法で回避できます。サーバー プッシュでバイナリ データを送信した直後に、サーバーは SSE をクライアントに送信して、データがキャッシュにプッシュされたことを通知します。これで、クライアントは要求することでキャッシュからデータを取得できます。

しかし、SSE を使用している限り、そもそもファイルを SSE 経由で送信してみませんか? バイナリ データを扱っている場合は、サーバー プッシュで実現できるファイル サイズを小さくできるというメリットがあります。短い JSON メッセージの場合、サーバー プッシュを使用しても意味がない場合があります。バイナリ データをプッシュしていて、帯域幅を節約する必要がある場合は、サーバー プッシュを介してデータを送信し、続いて SSE 通知を送信することを検討してください。

ポーリングとは異なり、このアプローチはクライアントからの定期的な要求を必要としません。サーバーは、必要なときにいつでもサーバー プッシュ応答を送信できます。

于 2015-08-29T08:43:18.517 に答える