每个主机的 Golang ReverseProxy

Golang ReverseProxy per host

我正在尝试在 Go 中实现一个反向代理,它根据 URL 中嵌入的一些租户将流量代理到不同的主机。实现如下所示:

type Offloader struct {
    tenantHostMap   map[string]string                   // Map a tenant to its host:port
    tenantProxyMap  map[string](*httputil.ReverseProxy) // Map a tenant to its reverse proxy
}

func (o *Offloader) OnCreate() {

    // Tenants Map
    o.tenantHostMap = make(map[string]string)
    o.tenantProxyMap = make(map[string]*httputil.ReverseProxy)
    o.PopulateTenantHostMap()

    // Rx
    http.HandleFunc("/", o.ServeHTTP)
    go http.ListenAndServe(":5555", nil)

}

// ServeHTTP is the callback that is called each time a Http Request is received.
func (o *Offloader) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    incomingUrl := req.URL.RequestURI()
    tenant := o.GetTenantFromUrl(incomingUrl)

    if proxy, ok := o.tenantProxyMap[tenant]; ok {
        proxy.ServeHTTP(w, req)
    }

    if remoteHostAddr, ok := o.tenantHostMap[tenant]; ok {
        remoteUrl, err := url.Parse(fmt.Sprintf("http://%s", remoteHostAddr))
        if err != nil {
            return
        }
        proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
        o.tenantProxyMap[tenant] = proxy
        proxy.ServeHTTP(w, req) // non blocking

    } else {
        panic("Unknown Tenant")
    }
}

当接收到新的 HTTP 请求时,我从 URL 获取租户。如果这是我第一次看到这个租户,我会创建一个新的 ReverseProxy,否则我会尝试使用我之前创建并存储在 tenantProxyMap.

中的那个

当我对此进行测试时,出现以下错误:

2022/04/05 12:31:01 http: proxy error: readfrom tcp ****: http: invalid Read on closed Body
2022/04/05 12:31:01 http: superfluous response.WriteHeader call from net/http/httputil.(*ReverseProxy).defaultErrorHandler (reverseproxy.go:190)

如果我为每个请求创建一个新的反向代理而不是重复使用相同的代理,则不会发生错误。

我认为代理是按主机而不是按请求(顾名思义),所以我想知道为什么会出现此错误?

我知道我需要保护地图不受并发影响 reads/writes 但是目前这无关紧要。

谢谢,

问题在于,在先前代理已经存在的情况下,您首先将请求传递给它 - 然后仍然重新创建代理,并再次传递请求。换句话说:当 tentantProxyMap 已经为该租户填充时,您正在为每个传入请求发出两个代理请求。

ReverseProxy 实现关闭 req.Body,因此您第二次将请求传递给代理时,它会尝试从已关闭的主体中读取。结果是 http: invalid Read on closed Body 错误。

您应该尝试 return 在代理请求之后,例如通过添加 return:

if proxy, ok := o.tenantProxyMap[tenant]; ok {
    proxy.ServeHTTP(w, req)
    return
}