获取”127.0.0.1无法分配请求的地址” – http.Client

huangapple go评论163阅读模式
英文:

Getting "127.0.0.1 can't assign requested address" - http.Client

问题

我正在做的事情相当简单。我需要创建一个非常简单和快速的“代理”服务器。目前我有一个基准服务器(使用nodejs)和一个代理服务(使用go)。请原谅实际上没有进行“代理”的部分-目前只是测试。

基准服务

var http = require('http');
http.createServer(function (req, res) {
    // console.log("received request");
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(8080, '127.0.0.1');
console.log('Server running at http://127.0.0.1:8080/');

代理服务

package main

import (
  "flag"
  "log"
  "net/http"
  "net/url"
)

var (
  listen = flag.String("listen", "0.0.0.0:9000", "listen on address")
  logp = flag.Bool("log", false, "enable logging")
)

func main() {
  flag.Parse()
  proxyHandler := http.HandlerFunc(proxyHandlerFunc)
  log.Fatal(http.ListenAndServe(*listen, proxyHandler))
  log.Println("Started router-server on 0.0.0.0:9000")
}

func proxyHandlerFunc(w http.ResponseWriter, r *http.Request) {
  // Log if requested
  if *logp {
    log.Println(r.URL)
  }

  /* 
   * Tweak the request as appropriate:
   *   - RequestURI may not be sent to client
   *   - Set new URL
   */
  r.RequestURI = ""
  u, err := url.Parse("http://localhost:8080/")
  if err != nil {
    log.Fatal(err)
  }
  r.URL = u

  // And proxy
  // resp, err := client.Do(r)
  c := make(chan *http.Response)
  go doRequest(c)
  resp := <-c
  if resp != nil {
    err := resp.Write(w)
    if err != nil {
      log.Println("Error writing response")
    } else {
      resp.Body.Close()
    }
  }
}


func doRequest(c chan *http.Response) {
  // new client for every request.
  client := &http.Client{}

  resp, err := client.Get("http://127.0.0.1:8080/test")
  if err != nil {
    log.Println(err)
    c <- nil
  } else {
    c <- resp
  }
}

正如标题中提到的,我的问题是在doRequest函数中出现错误,错误信息为2013/10/28 21:22:30 Get http://127.0.0.1:8080/test: dial tcp 127.0.0.1:8080: can't assign requested address,我不知道为什么会出现这个错误。在谷歌上搜索这个特定的错误会得到看似无关的结果。

英文:

What I'm doing is fairly straight-forward. I need to create a "proxy" server that is very minimal and fast. Currently I have a baseline server that is proxied to (nodejs) and a proxy-service (go). Please excuse the lack of actual "proxy'ing" - just testing for now.

Baseline Service

var http = require(&#39;http&#39;);
http.createServer(function (req, res) {
// console.log(&quot;received request&quot;);
res.writeHead(200, {&#39;Content-Type&#39;: &#39;text/plain&#39;});
res.end(&#39;Hello World\n&#39;);
}).listen(8080, &#39;127.0.0.1&#39;);
console.log(&#39;Server running at http://127.0.0.1:8080/&#39;);

Proxy Service

package main
import (
&quot;flag&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;net/url&quot;
)
var (
listen = flag.String(&quot;listen&quot;, &quot;0.0.0.0:9000&quot;, &quot;listen on address&quot;)
logp = flag.Bool(&quot;log&quot;, false, &quot;enable logging&quot;)
)
func main() {
flag.Parse()
proxyHandler := http.HandlerFunc(proxyHandlerFunc)
log.Fatal(http.ListenAndServe(*listen, proxyHandler))
log.Println(&quot;Started router-server on 0.0.0.0:9000&quot;)
}
func proxyHandlerFunc(w http.ResponseWriter, r *http.Request) {
// Log if requested
if *logp {
log.Println(r.URL)
}
/* 
* Tweak the request as appropriate:
*   - RequestURI may not be sent to client
*   - Set new URL
*/
r.RequestURI = &quot;&quot;
u, err := url.Parse(&quot;http://localhost:8080/&quot;)
if err != nil {
log.Fatal(err)
}
r.URL = u
// And proxy
// resp, err := client.Do(r)
c := make(chan *http.Response)
go doRequest(c)
resp := &lt;-c
if resp != nil {
err := resp.Write(w)
if err != nil {
log.Println(&quot;Error writing response&quot;)
} else {
resp.Body.Close()
}
}
}
func doRequest(c chan *http.Response) {
// new client for every request.
client := &amp;http.Client{}
resp, err := client.Get(&quot;http://127.0.0.1:8080/test&quot;)
if err != nil {
log.Println(err)
c &lt;- nil
} else {
c &lt;- resp
}
}

My issue, as mentioned within the title, is that I am getting errors stating 2013/10/28 21:22:30 Get http://127.0.0.1:8080/test: dial tcp 127.0.0.1:8080: can&#39;t assign requested address from the doRequest function, and I have no clue why. Googling this particular error yields seemingly irrelevant results.

答案1

得分: 5

这段代码存在两个主要问题:

  1. 你没有处理客户端的停顿或使用保持活动连接(由getTimeoutServer处理)。
  2. 你没有处理服务器(你的http.Client正在与之通信的服务器)超时(由TimeoutConn处理)。

这可能是你耗尽本地端口的原因。我从过去的经验中知道,node.js 会非常积极地保持连接活动。

还有很多小问题,比如在不需要的情况下每次创建对象,创建不必要的 goroutines(在处理之前,每个传入的请求都在自己的 goroutine 中)。

这是一个快速的尝试(我没有时间进行充分测试)。希望它能帮助你找到正确的方向:(你可能需要升级它,以便不在本地缓冲响应)

package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"strconv"
	"time"
)

const DEFAULT_IDLE_TIMEOUT = 5 * time.Second

var (
	listen       string
	logOn        bool
	localhost, _ = url.Parse("http://localhost:8080/")
	client       = &http.Client{
		Transport: &http.Transport{
			Proxy: NoProxyAllowed,
			Dial: func(network, addr string) (net.Conn, error) {
				return NewTimeoutConnDial(network, addr, DEFAULT_IDLE_TIMEOUT)
			},
		},
	}
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.StringVar(&listen, "listen", "0.0.0.0:9000", "listen on address")
	flag.BoolVar(&logOn, "log", true, "enable logging")
	flag.Parse()
	server := getTimeoutServer(listen, http.HandlerFunc(proxyHandlerFunc))
	log.Printf("Starting router-server on %s\n", listen)
	log.Fatal(server.ListenAndServe())
}

func proxyHandlerFunc(w http.ResponseWriter, req *http.Request) {
	if logOn {
		log.Printf("%+v\n", req)
	}
	// Setup request URL
	origURL := req.URL
	req.URL = new(url.URL)
	*req.URL = *localhost
	req.URL.Path, req.URL.RawQuery, req.URL.Fragment = origURL.Path, origURL.RawQuery, origURL.Fragment
	req.RequestURI, req.Host = "", req.URL.Host
	// Perform request
	resp, err := client.Do(req)
	if err != nil {
		w.WriteHeader(http.StatusBadGateway)
		w.Write([]byte(fmt.Sprintf("%d - StatusBadGateway: %s", http.StatusBadGateway, err)))
		return
	}
	defer resp.Body.Close()
	var respBuffer *bytes.Buffer
	if resp.ContentLength != -1 {
		respBuffer = bytes.NewBuffer(make([]byte, 0, resp.ContentLength))
	} else {
		respBuffer = new(bytes.Buffer)
	}
	if _, err = respBuffer.ReadFrom(resp.Body); err != nil {
		w.WriteHeader(http.StatusBadGateway)
		w.Write([]byte(fmt.Sprintf("%d - StatusBadGateway: %s", http.StatusBadGateway, err)))
		return
	}
	// Write result of request
	headers := w.Header()
	var key string
	var val []string
	for key, val = range resp.Header {
		headers[key] = val
	}
	headers.Set("Content-Length", strconv.Itoa(respBuffer.Len()))
	w.WriteHeader(resp.StatusCode)
	w.Write(respBuffer.Bytes())
}

func getTimeoutServer(addr string, handler http.Handler) *http.Server {
	//keeps people who are slow or are sending keep-alives from eating all our sockets
	const (
		HTTP_READ_TO  = DEFAULT_IDLE_TIMEOUT
		HTTP_WRITE_TO = DEFAULT_IDLE_TIMEOUT
	)
	return &http.Server{
		Addr:         addr,
		Handler:      handler,
		ReadTimeout:  HTTP_READ_TO,
		WriteTimeout: HTTP_WRITE_TO,
	}
}

func NoProxyAllowed(request *http.Request) (*url.URL, error) {
	return nil, nil
}

//TimeoutConn-------------------------
//Put me in my own TimeoutConn.go ?

type TimeoutConn struct {
	net.Conn
	readTimeout, writeTimeout time.Duration
}

var invalidOperationError = errors.New("TimeoutConn does not support or allow .SetDeadline operations")

func NewTimeoutConn(conn net.Conn, ioTimeout time.Duration) (*TimeoutConn, error) {
	return NewTimeoutConnReadWriteTO(conn, ioTimeout, ioTimeout)
}

func NewTimeoutConnReadWriteTO(conn net.Conn, readTimeout, writeTimeout time.Duration) (*TimeoutConn, error) {
	this := &TimeoutConn{
		Conn:         conn,
		readTimeout:  readTimeout,
		writeTimeout: writeTimeout,
	}
	now := time.Now()
	err := this.Conn.SetReadDeadline(now.Add(this.readTimeout))
	if err != nil {
		return nil, err
	}
	err = this.Conn.SetWriteDeadline(now.Add(this.writeTimeout))
	if err != nil {
		return nil, err
	}
	return this, nil
}

func NewTimeoutConnDial(network, addr string, ioTimeout time.Duration) (net.Conn, error) {
	conn, err := net.DialTimeout(network, addr, ioTimeout)
	if err != nil {
		return nil, err
	}
	if conn, err = NewTimeoutConn(conn, ioTimeout); err != nil {
		return nil, err
	}
	return conn, nil
}

func (this *TimeoutConn) Read(data []byte) (int, error) {
	this.Conn.SetReadDeadline(time.Now().Add(this.readTimeout))
	return this.Conn.Read(data)
}

func (this *TimeoutConn) Write(data []byte) (int, error) {
	this.Conn.SetWriteDeadline(time.Now().Add(this.writeTimeout))
	return this.Conn.Write(data)
}

func (this *TimeoutConn) SetDeadline(time time.Time) error {
	return invalidOperationError
}

func (this *TimeoutConn) SetReadDeadline(time time.Time) error {
	return invalidOperationError
}

func (this *TimeoutConn) SetWriteDeadline(time time.Time) error {
	return invalidOperationError
}

希望对你有帮助!

英文:

There are 2 major problems with this code.

  1. You are not handling the client stalling or using keep alives (handled below by getTimeoutServer)
  2. You are not handling the server (what your http.Client is talking to) timing out (handled below by TimeoutConn).

This is probably why you are exhausting your local ports. I know from past experience node.js will keep-alive you very aggressively.

There are lots of little issues, creating objects every-time when you don't need to. Creating unneeded goroutines (each incoming request is in its own goroutine before you handle it).

Here is a quick stab (that I don't have time to test well). Hopefully it will put you on the right track: (You will want to upgrade this to not buffer the responses locally)

package main
import (
&quot;bytes&quot;
&quot;errors&quot;
&quot;flag&quot;
&quot;fmt&quot;
&quot;log&quot;
&quot;net&quot;
&quot;net/http&quot;
&quot;net/url&quot;
&quot;runtime&quot;
&quot;strconv&quot;
&quot;time&quot;
)
const DEFAULT_IDLE_TIMEOUT = 5 * time.Second
var (
listen       string
logOn        bool
localhost, _ = url.Parse(&quot;http://localhost:8080/&quot;)
client       = &amp;http.Client{
Transport: &amp;http.Transport{
Proxy: NoProxyAllowed,
Dial: func(network, addr string) (net.Conn, error) {
return NewTimeoutConnDial(network, addr, DEFAULT_IDLE_TIMEOUT)
},
},
}
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.StringVar(&amp;listen, &quot;listen&quot;, &quot;0.0.0.0:9000&quot;, &quot;listen on address&quot;)
flag.BoolVar(&amp;logOn, &quot;log&quot;, true, &quot;enable logging&quot;)
flag.Parse()
server := getTimeoutServer(listen, http.HandlerFunc(proxyHandlerFunc))
log.Printf(&quot;Starting router-server on %s\n&quot;, listen)
log.Fatal(server.ListenAndServe())
}
func proxyHandlerFunc(w http.ResponseWriter, req *http.Request) {
if logOn {
log.Printf(&quot;%+v\n&quot;, req)
}
// Setup request URL
origURL := req.URL
req.URL = new(url.URL)
*req.URL = *localhost
req.URL.Path, req.URL.RawQuery, req.URL.Fragment = origURL.Path, origURL.RawQuery, origURL.Fragment
req.RequestURI, req.Host = &quot;&quot;, req.URL.Host
// Perform request
resp, err := client.Do(req)
if err != nil {
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(fmt.Sprintf(&quot;%d - StatusBadGateway: %s&quot;, http.StatusBadGateway, err)))
return
}
defer resp.Body.Close()
var respBuffer *bytes.Buffer
if resp.ContentLength != -1 {
respBuffer = bytes.NewBuffer(make([]byte, 0, resp.ContentLength))
} else {
respBuffer = new(bytes.Buffer)
}
if _, err = respBuffer.ReadFrom(resp.Body); err != nil {
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(fmt.Sprintf(&quot;%d - StatusBadGateway: %s&quot;, http.StatusBadGateway, err)))
return
}
// Write result of request
headers := w.Header()
var key string
var val []string
for key, val = range resp.Header {
headers[key] = val
}
headers.Set(&quot;Content-Length&quot;, strconv.Itoa(respBuffer.Len()))
w.WriteHeader(resp.StatusCode)
w.Write(respBuffer.Bytes())
}
func getTimeoutServer(addr string, handler http.Handler) *http.Server {
//keeps people who are slow or are sending keep-alives from eating all our sockets
const (
HTTP_READ_TO  = DEFAULT_IDLE_TIMEOUT
HTTP_WRITE_TO = DEFAULT_IDLE_TIMEOUT
)
return &amp;http.Server{
Addr:         addr,
Handler:      handler,
ReadTimeout:  HTTP_READ_TO,
WriteTimeout: HTTP_WRITE_TO,
}
}
func NoProxyAllowed(request *http.Request) (*url.URL, error) {
return nil, nil
}
//TimeoutConn-------------------------
//Put me in my own TimeoutConn.go ?
type TimeoutConn struct {
net.Conn
readTimeout, writeTimeout time.Duration
}
var invalidOperationError = errors.New(&quot;TimeoutConn does not support or allow .SetDeadline operations&quot;)
func NewTimeoutConn(conn net.Conn, ioTimeout time.Duration) (*TimeoutConn, error) {
return NewTimeoutConnReadWriteTO(conn, ioTimeout, ioTimeout)
}
func NewTimeoutConnReadWriteTO(conn net.Conn, readTimeout, writeTimeout time.Duration) (*TimeoutConn, error) {
this := &amp;TimeoutConn{
Conn:         conn,
readTimeout:  readTimeout,
writeTimeout: writeTimeout,
}
now := time.Now()
err := this.Conn.SetReadDeadline(now.Add(this.readTimeout))
if err != nil {
return nil, err
}
err = this.Conn.SetWriteDeadline(now.Add(this.writeTimeout))
if err != nil {
return nil, err
}
return this, nil
}
func NewTimeoutConnDial(network, addr string, ioTimeout time.Duration) (net.Conn, error) {
conn, err := net.DialTimeout(network, addr, ioTimeout)
if err != nil {
return nil, err
}
if conn, err = NewTimeoutConn(conn, ioTimeout); err != nil {
return nil, err
}
return conn, nil
}
func (this *TimeoutConn) Read(data []byte) (int, error) {
this.Conn.SetReadDeadline(time.Now().Add(this.readTimeout))
return this.Conn.Read(data)
}
func (this *TimeoutConn) Write(data []byte) (int, error) {
this.Conn.SetWriteDeadline(time.Now().Add(this.writeTimeout))
return this.Conn.Write(data)
}
func (this *TimeoutConn) SetDeadline(time time.Time) error {
return invalidOperationError
}
func (this *TimeoutConn) SetReadDeadline(time time.Time) error {
return invalidOperationError
}
func (this *TimeoutConn) SetWriteDeadline(time time.Time) error {
return invalidOperationError
}

答案2

得分: 5

我们遇到了这个问题,在花费了很多时间进行调试之后,我找到了这个链接:https://code.google.com/p/go/source/detail?r=d4e1ec84876c

> 这将把读取整个响应体的负担转移到客户端,如果他们想要重用TCP连接的优势。

所以在关闭之前,请确保读取整个响应体,有几种方法可以做到。这个函数可以派上用场,它可以通过记录未读取的额外字节并清理流来帮助您查看是否存在此问题,以便可以重用连接:

func closeResponse(response *http.Response) error {
// 确保我们读取了整个响应体
bs, err2 := ioutil.ReadAll(response.Body)
if err2 != nil {
log.Println("ReadAll过程中出错了!", err2)
}
if len(bs) > 0 {
log.Println("不得不读取一些字节,不好!", bs, string(bs))
}
return response.Body.Close()
}

或者,如果您真的不关心响应体,可以使用以下代码将其丢弃:

io.Copy(ioutil.Discard, response.Body)
英文:

We ran into this and after a lot of time trying to debug, I came across this: https://code.google.com/p/go/source/detail?r=d4e1ec84876c

> This shifts the burden onto clients to read their whole response
bodies if they want the advantage of reusing TCP connections.

So be sure you read the entire body before closing, there are a couple of ways to do it. This function can come in handy to close to let you see whether you have this issue by logging the extra bytes that haven't been read and cleaning the stream out for you so it can reuse the connection:

func closeResponse(response *http.Response) error {
// ensure we read the entire body
bs, err2 := ioutil.ReadAll(response.Body)
if err2 != nil {
log.Println(&quot;Error during ReadAll!!&quot;, err2)
}
if len(bs) &gt; 0 {
log.Println(&quot;Had to read some bytes, not good!&quot;, bs, string(bs))
}
return response.Body.Close()
}

Or if you really don't care about the body, you can just discard it with this:

io.Copy(ioutil.Discard, response.Body)

答案3

得分: 1

我也遇到了这个问题,我在http.Transport中添加了一个选项{DisableKeepAlives: true}来解决这个问题,你可以试一试。

英文:

I have encountered this problem too, and i add an option {DisableKeepAlives: true} to http.Transport fixed this issue, you can have a try.

答案4

得分: 1

我在没有限制空闲连接数量的情况下,在一个系统上每秒运行大量的SQL查询时来到这里。正如在GitHub上的这个问题评论中指出的那样,明确设置db.SetMaxIdleConns(5)完全解决了我的问题。

英文:

I came here when running a massive amount of SQL queries per second on a system without limiting the number of idle connections over a long period of time. As pointed out in this issue comment on github explicitly setting db.SetMaxIdleConns(5) completely solved my problem.

huangapple
  • 本文由 发表于 2013年10月29日 09:43:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/19648217.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定