实现简单的http反向代理 还没有加入负载均衡 新手推荐
下游服务器代码
package mainimport ("fmt""io""log""net/http""os""os/signal""syscall""time"
)type RealServer struct {Addr string
}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 (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {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 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
}
反向代理代码
主要的思路是拿到httputil.ReverseProxy,修改里面内置的一些方法
该结构体有ServeHttp接口 因此可以直接使用到funhandlc中
package mainimport ("bytes""errors""fmt""io""log""net/http""net/http/httputil""net/url""regexp""strings"
)// 当前服务器地址
var addr = "127.0.0.1:2222"func main() {//设置下游的地址rs1 := "http://127.0.0.1:2003"url1, err := url.Parse(rs1)if err != nil {log.Println(err)}proxy := NewSingleHostReverseProxy(url1)log.Println("Starting httpserver at " + addr)log.Fatal(http.ListenAndServe(addr, proxy))
}func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {targetQuery := target.RawQuery//设置新的请求信息director := func(req *http.Request) {//url_rewrite//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abcre, _ := regexp.Compile("^/dir(.*)")req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")req.URL.Scheme = target.Schemereq.URL.Host = target.Host//target.Path : /base//req.URL.Path : /dirreq.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 {req.Header.Set("User-Agent", "")}}//修改返回值modifyFunc := func(res *http.Response) error {if res.StatusCode != 200 {return errors.New("error statusCode")}oldPayload, err := io.ReadAll(res.Body)if err != nil {return err}newPayLoad := []byte("hello " + string(oldPayload))res.Body = io.NopCloser(bytes.NewBuffer(newPayLoad))res.ContentLength = int64(len(newPayLoad))res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))return nil}//错误处理errorHandler := func(res http.ResponseWriter, req *http.Request, err error) {res.Write([]byte(err.Error()))}return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc, ErrorHandler: errorHandler}
}// 重新组合URL
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
}
通过反向代理修改返回结果 添加了hello
oldPayload, err := io.ReadAll(res.Body)if err != nil {return err}newPayLoad := []byte("hello " + string(oldPayload))res.Body = io.NopCloser(bytes.NewBuffer(newPayLoad))res.ContentLength = int64(len(newPayLoad))res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))
X-Forwarded-For 头部
记录整个代理过程的IP
可能会被仿造 也就是发送请求的时候改变X-Forwarded-For的内容
X-Real-IP 头部
拿到请求的真实服务器的IP
每次代理都会被覆盖,只需要在第一层代理设置转发
不会被伪造