通过对等方重置httputil.ReverseProxy连接

时间:2016-12-23 11:12:38

标签: http go proxy

我的任务是创建一个反向代理应用程序,它从请求/响应主体中提取一些特定信息并将其发送到logstash。所以我决定先做一些负载测试。我编写了非常简单的后端Web服务器和基本代理。

后端服务器代码:

package main

import "net/http"

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":3001", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
    r.Body.Close()
    w.Write([]byte("test"))
}

反向代理服务器代码:

package main

import (
    "net/http"
    "net/http/httputil"
    "net/url"
)

var (
    proxy *httputil.ReverseProxy
)

func init() {
    url, _ := url.Parse("http://localhost:3001")
    proxy = httputil.NewSingleHostReverseProxy(url)
    proxy.Transport = &mimTransport{}
}

func main() {
    http.ListenAndServe(":3000", proxy)
}

func handler(w http.ResponseWriter, r *http.Request) {
    r.Host = "localhost:3001"
    proxy.ServeHTTP(w, r)
}

type mimTransport struct {
}

func (t *mimTransport) RoundTrip(request *http.Request) (*http.Response, error) {
    response, err := http.DefaultTransport.RoundTrip(request)
    if response != nil {
        defer request.Body.Close()
    }

    // info extraction will be here

    return response, err
}

正如您可以看到后端服务器正在监听localhost:3001和代理监听localhost:3000

我正在使用apache基准进行负载测试。

当我在12k-14k左右运行$ ab -c 20 -n 20000 -s 10 http://127.0.0.1:3000/时,请求代理开始写错误,如:

2016/12/23 13:02:50 http: proxy error: read tcp [::1]:58330->[::1]:3001: read: connection reset by peer
2016/12/23 13:02:50 http: proxy error: read tcp [::1]:58461->[::1]:3001: read: connection reset by peer
2016/12/23 13:02:50 http: proxy error: read tcp [::1]:58977->[::1]:3001: read: connection reset by peer
2016/12/23 13:02:50 http: proxy error: read tcp [::1]:59459->[::1]:3001: read: connection reset by peer
...
不久之后,apache替补席因超时而死。一些替补结果:

Benchmarking 127.0.0.1 (be patient)
Completed 2000 requests
Completed 4000 requests
Completed 6000 requests
Completed 8000 requests
Completed 10000 requests
^C

Server Software:        
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /
Document Length:        4 bytes

Concurrency Level:      20
Time taken for tests:   4.868 seconds
Complete requests:      11514
Failed requests:        22
   (Connect: 0, Receive: 0, Length: 22, Exceptions: 0)
Non-2xx responses:      22
Total transferred:      1381790 bytes
HTML transferred:       45968 bytes
Requests per second:    2365.28 [#/sec] (mean)
Time per request:       8.456 [ms] (mean)
Time per request:       0.423 [ms] (mean, across all concurrent requests)
Transfer rate:          277.20 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   4.2      1     111
Processing:     0    3   4.8      3     113
Waiting:        0    3   4.7      3     113
Total:          0    4   6.4      4     116

Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      5
  80%      5
  90%      5
  95%      6
  98%      6
  99%      7
 100%    116 (longest request)

还有更多,如果我在第一次测试结束后再次启动ab,我会收到类似的内容:

Benchmarking 127.0.0.1 (be patient)
apr_pollset_poll: The timeout specified has expired (70007)
Total of 52 requests completed

(也许以前测试中的一些连接还活着)

互联网上说Go中有一些DoS保护机制,显然它是重置连接的原因。 所以我的问题是:

  1. 为什么它重置连接(读取缓冲区已满,超时发生,连接太多或者我的代码可能已损坏)
  2. 我可以调整这个机制吗?例如,更改max timeout的扩展缓冲区。
  3. 一些提示如何正确处理此类错误。
  4. 感谢。

0 个答案:

没有答案