ReverseProxy功能点
- 更改内容支持
- 错误信息回调
- 支持自定义负载均衡
- url重写
- 连接池
真机侧
real_server.go
package main
type RealServer struct {
Addr string
}
func NewRealServer(addr string) *RealServer {
return &RealServer{
Addr: addr}
}
func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {
//127.0.0.1:8008/abc?sdsdsa=11
//r.Addr=127.0.0.1:8008
//req.URL.Path=/abc
//fmt.Println(req.Host)
req.Header.Set("X-Forwarded-For", req.RemoteAddr)
upath := fmt.Sprintf("http://%s%s\n", r.Addr, req.URL.Path)
realIP := fmt.Sprintf("RemoteAddr=%s,X-Forwarded-For=%v,X-Real-Ip=%v\n", req.RemoteAddr, req.Header.Get("X-Forwarded-For"), req.Header.Get("X-Real-Ip"))
header := fmt.Sprintf("headers =%v\n", req.Header)
io.WriteString(w, upath)
io.WriteString(w, realIP)
io.WriteString(w, header)
}
func (r *RealServer) ErrorHandler(w http.ResponseWriter, req *http.Request) {
upath := "error handler"
w.WriteHeader(500)
io.WriteString(w, upath)
}
func (r *RealServer) TimeoutHandler(w http.ResponseWriter, req *http.Request) {
time.Sleep(6 * time.Second)
upath := "timeout handler"
w.WriteHeader(200)
io.WriteString(w, upath)
}
func (r *RealServer) Run() {
log.Println("Starting httpserver at " + r.Addr)
mux := http.NewServeMux()
mux.HandleFunc("/", r.HelloHandler)
mux.HandleFunc("/base/error", r.ErrorHandler)
mux.HandleFunc("/test_http_string/test_http_string/aaa", r.TimeoutHandler)
server := &http.Server{
Addr: r.Addr,
WriteTimeout: time.Second * 3,
Handler: mux,
}
go func() {
log.Fatal(server.ListenAndServe())
}()
}
func main() {
rs1 := &RealServer{
Addr: "127.0.0.1:2003"}
rs1.Run()
rs2 := &RealServer{
Addr: "127.0.0.1:2004"}
rs2.Run()
//监听关闭信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
}
第一层代理
reverse_proxy_level1
package main
import mrand "math/rand"
var (
proxyAddr = "0.0.0.0:2001"
nextProxy = "http://127.0.0.1:2002"
transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, //连接超时
KeepAlive: 30 * time.Second, //长连接超时时间
}).DialContext,
MaxIdleConns: 100, //最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls握手超时时间
ExpectContinueTimeout: 1 * time.Second, //100-continue 超时时间
}
)
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
// NewMultipleHostsReverseProxy ...
func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {
director := func(req *http.Request) {
// 1. 随机负载均衡
index := mrand.Intn(len(targets))
target := targets[index]
targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
// 2. url地址重写
re, _ := regexp.Compile("^/dir(.*)")
req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
// 3.只在第一代理设置此header头
req.Header.Set("X-Real-IP", req.RemoteAddr)
}
//更改内容
modifyFunc := func(resp *http.Response) error {
if resp.StatusCode != 200 {
oldPlayload, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
newPlayload := []byte("StatusCode error: " + string(oldPlayload))
resp.Body = ioutil.NopCloser(bytes.NewBuffer(newPlayload))
resp.ContentLength = int64(len(newPlayload))
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(newPlayload)), 10))
}
return nil
}
//错误回调: 关闭real_server时测试,错误回调
errFunc := func(rw http.ResponseWriter, req *http.Request, err error) {
http.Error(rw, "ErrorHandler error: "+err.Error(), 500)
}
return &httputil.ReverseProxy{
Director: director,
Transport: transport,
ModifyResponse: modifyFunc,
ErrorHandler: errFunc,
}
}
func main() {
url1, err := url.Parse(nextProxy)
if err != nil {
log.Println(err)
}
urls := []*url.URL{
url1}
proxy := NewMultipleHostsReverseProxy(urls)
log.Println("start server on ", proxyAddr)
log.Fatal(http.ListenAndServe(proxyAddr, proxy))
}
二层代理
````reverse_proxy.go``
package main
import mrand "math/rand"
var (
addr = "0.0.0.0:2002"
real1 = "http://127.0.0.1:2003"
real2 = "http://127.0.0.1:2004"
transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, //连接超时
KeepAlive: 30 * time.Second, //长连接超时时间
}).DialContext,
MaxIdleConns: 100, //最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls握手超时时间
ExpectContinueTimeout: 1 * time.Second, //100-continue 超时时间
}
)
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
// NewMultipleHostsReverseProxy ...
func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {
//请求协调者
director := func(req *http.Request) {
//随机负载均衡
index := mrand.Intn(len(targets))
target := targets[index]
targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
// url重写
re, _ := regexp.Compile("^/dir")
req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
}
// 修改返回内容
modifyFunc := func(resp *http.Response) error {
oldPlayload, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
newPlayload := []byte("second proxy" + string(oldPlayload))
resp.Body = ioutil.NopCloser(bytes.NewBuffer(newPlayload))
resp.ContentLength = int64(len(newPlayload))
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(newPlayload)), 10))
return nil
}
return &httputil.ReverseProxy{
Director: director,
Transport: transport,
ModifyResponse: modifyFunc,
}
}
func main() {
url1, _ := url.Parse(real1)
url2, _ := url.Parse(real2)
urls := []*url.URL{
url1, url2}
proxy := NewMultipleHostsReverseProxy(urls) //代理谁,代理后端服务器的url
log.Println("start server on ", addr)
log.Fatal(http.ListenAndServe(addr, proxy))
}
随机请求后端2003|2004