LoginSignup
0

More than 1 year has passed since last update.

posted at

updated at

Organization

Go言語のリバプロでのちょっとしたテクニックメモ

Go言語でReverse Proxyを構築する際に気づいたテクニックをメモしておきます。
最適解ではないかもですが、自分はこのように構築してOKをもらいました。

やりたいこと

  • リバプロで受け取ったリクエストを処理(シグネチャ生成等)したとき、errorになる場合は、リバプロからエラーレスポンスを返す。
    • エラーレスポンスはリバプロで生成
  • errorにならない場合は、バックエンドからの応答をそのまま返す。
    • リバプロでのレスポンス加工は行わない

指摘されたこと

疑似コードで説明すると、最初はこのような感じで実装しました。

func main() {
    var err error
    director := func(request *http.Request) {
        err = // シグネチャ生成などの処理
    }

    modifier := func(res *http.Response) error {
        if err != nil{
            // エラーレスポンスのフォーマットで返す
        } else {
            // バックエンドからの応答をそのまま返す
        }
    }

    rp := &httputil.ReverseProxy{
        Director: director,
        ModifyResponse: modifier,
    }
    server := http.Server{
        Addr:    ":9000",
        Handler: rp,
    }
}

この場合、以下のような懸念を指摘して頂きました。

  • リクエストAがerrorを出していないにも関わらず、同時に受信したリクエストBが出したerrorが原因でリクエストAがエラーになるかも。。。

どのように対処したか

要点は以下の通りです。

  • directorで何かの処理をした後、requestを返すようにする
  • errorが発生しなかった場合はdirectorからバックエンドにリクエストを投げる
    • レスポンスはそのまま返す
  • 処理中にerrorが発生した場合は、localhost:5001/dummyからエラーレスポンスのフォーマットに合わせたレスポンスを返す
    (directorから/dummyへリクエストして、レスポンスをそのまま返す)
    • エラーレスポンスはdummyHundlerで用意
    • エラーレスポンスで必要な値はクエリに記載する

このような感じで実装しました。(疑似コード)

// エラーレスポンスのJson
type ProxyErr struct{
    hoge string
}

func main() {
    director := func(request *http.Request) {
        var nerReq *http.Request
        var statuCode int
        var err error
        newReq, statuCode, err = // 何かの処理

        // エラーレスポンスを返す場合
        if err != nil{
            endpoint = "http://localhost:5001/dummy"
            u, err := url.parse(endpoint)
            q := u.Query()
            q.set("statuCode", strconv.Itoa(statuCode))
            q.set("hoge", value)
            u.RawQuery = q.encode()
            newReq = http.NewRequest("GET", u.string(), nil)
        }
        *request = *newReq
    }

    rp := &httputil.ReverseProxy{
        Director: director,
        // レスポンスの加工は行わないので、modifierは不要となる
    }
    server := http.Server{
        Addr:    ":9000",
        Handler: rp,
    }

    dummyHundler =:= func(w http.ResponseWriter, req *http.Request){
        var statuCode int
        statuCode , _ = strconv.Atoi(req.URL.Query().Get("statuCode"))
        w.writeHeader(statuCode)  // エラーレスポンスなので、ステータスコードを明示的に変更します
        w.Header().Set("Context-Type" "application/json")  // jsonフォーマットの場合はContext-Typeを明示的に指定します
        response := ProxyErr{
            hoge: req.URL.Query().Get("hoge"),
        }
        res, _ = json.Marshal(response)
        w.Write(res)
    }

    go func(){
        http.HandleFunc("/dummy", dummyHundler)
        http.ListenAndServe("localhost:5001", nil)
    }

    go func(){
        err := server.ListenAndServe()
        if err != nil{
            // エラー処理
        }
    }
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0