正向代理
package main /** 正向代理 */ import ( "net/http" "fmt" "net" "strings" "io" ) type Proxy struct { } func (p *Proxy) ServeHTTP(w http.ResponseWriter, req *http.Request){ fmt.Printf("接受请求 %s %s %s\n",req.Method,req.Host,req.RemoteAddr) transport:=http.DefaultTransport // 第一步: 代理接受到客户端的请求,复制原来的请求对象,并根据数据配置新请求的各种参数(添加上X-Forward-For头部等) outReq:=new(http.Request) *outReq = *req // 这只是一个浅层拷贝 clientIP,_,err:=net.SplitHostPort(req.RemoteAddr) if err==nil{ prior,ok:=outReq.Header["X-Forwarded-For"] if ok { clientIP = strings.Join(prior,", ")+", "+clientIP } outReq.Header.Set("X-Forwarded-For",clientIP) } // 第二步: 把新请求复制到服务器端,并接收到服务器端返回的响应 res,err:=transport.RoundTrip(outReq) if err!=nil{ w.WriteHeader(http.StatusBadGateway) // 502 return } // 第三步:代理服务器对响应做一些处理,然后返回给客户端 for key,value:=range res.Header{ for _,v:=range value{ w.Header().Add(key,v) } } w.WriteHeader(res.StatusCode) io.Copy(w,res.Body) res.Body.Close() } func main() { fmt.Println("Serve on :8080") http.Handle("/",&Proxy{}) http.ListenAndServe("0.0.0.0:8080",nil) } // 代码运行之后,会在本地的 8080 端口启动代理服务。修改浏览器的代理为 127.0.0.1::8080 // 再访问网站,可以验证代理正常工作,也能看到它在终端打印出所有的请求信息。
反向代理
package main import ( "net/url" "net/http/httputil" "net/http" "math/rand" "log" ) /** 反向代理:编写反向代理按照上面的思路当然没有问题,只需要在第二步的时候,根据之前的配置修改 outReq 的 URL Host 地址可以了。 不过 Golang 已经给我们提供了编写代理的框架: httputil.ReverseProxy 。我们可以用非常简短的代码来实现自己的代理,而且内 部的细节问题都已经被很好地处理了。 实现一个简单的反向代理,它能够对请求实现负载均衡,随机地把请求发送给某些配置好的后端服务器。使用 httputil.ReverseProxy 编写反向代理最重要的就是实现自己的 Director 对象,这是 GoDoc 对它的介绍 Director必须是一个功能,它将请求修改为使用Transport发送的新请求。然后将其响应未经修改地复制回原始客户端。 Director返回 后不得访问提供的请求。 简单翻译的话, Director 是一个函数,它接受一个请求作为参数,然后对其进行修改。修改后的请求会实际发送给服务器端,因此我们编 写自己的 Director 函数,每次把请求的 Scheme 和 Host 修改成某个后端服务器的地址,就能实现负载均衡的效果(其实上面的正向代理也可以通过相同的方法实现) */ func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy{ director:= func(req *http.Request) { target:=targets[rand.Int()*len(targets)] req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path = target.Path } return &httputil.ReverseProxy{ Director:director, } } func main() { proxy:=NewMultipleHostsReverseProxy([]*url.URL{ { Scheme:"http", Host:"localhost:9091", }, { Scheme:"http", Host:"localhost:9092", }, }) log.Fatal(http.ListenAndServe(":9090",proxy)) } // 让代理监听在 9090 端口,在后端启动两个返回不同响应的服务器分别监听 // 在 9091 和 9092 端口,通过 curl 访问,可以看到多次请求会返回不同的结果。 // curl http://127.0.0.1:9090 // curl http://127.0.0.1:9090