实现简单的http反向代理 还没有加入负载均衡 新手推荐
下游服务器代码
package main
import (
"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 main
import (
"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/abc
re, _ := regexp.Compile("^/dir(.*)")
req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
//target.Path : /base
//req.URL.Path : /dir
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 {
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
每次代理都会被覆盖,只需要在第一层代理设置转发
不会被伪造