LoginSignup
8
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-12-12

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{
            // エラー処理
        }
    }
}
8
0
0

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
  3. You can use dark theme
What you can do with signing up
8
0