概念

正向代理

正向代理是一种客户端代理技术,帮助客户端访问无法访问的服务器资源,可以隐藏客户端的真实ip

反向代理

服务器端的代理技术,帮助服务器负载均衡、缓存、安全校验,可以隐藏服务器的真实ip

正向代理实现

实现原理:正向代理服务器接收到客户端到request请求,把request拷贝为自己的request发送给服务器,并把服务器返回的数据包装给客户端

type Pxy struct {

}



func (p *Pxy) ServeHTTP(rw http.ResponseWriter,req *http.Request) {

	fmt.Printf("正向代理接收到请求:%s %s %s\n",req.Method,req.Host,req.RemoteAddr)

	transport:=http.DefaultTransport
	//浅拷贝
	outReq:=new (http.Request)
	*outReq = *req
	if clientIP,_,err :=net.SplitHostPort(req.RemoteAddr); err==nil{
		if prior,ok:=outReq.Header["X-Forwarded-For"];ok{
			clientIP = strings.Join(prior,", ")+", "+clientIP
		}
		outReq.Header.Set("X-Forwarded-For",clientIP)
	}

	//把消息利用该正向代理服务器发送给下游
	res,err:=transport.RoundTrip(outReq)
	if err!=nil{
		rw.WriteHeader(http.StatusBadGateway)
		return
	}

	//把下游得到的数据返回给上游

	//复制头部信息
	for key,value:=range res.Header{
		for _,v := range value{
			rw.Header().Add(key,v)
		}

	}
	rw.WriteHeader(res.StatusCode)
	
	io.Copy(rw,res.Body)
	res.Body.Close()
}

func main() {
	fmt.Println("服务器正向代理启动:8080")
	http.Handle("/",&Pxy{})
	http.ListenAndServe("0.0.0.0:8080",nil)
}

这段代码涉及的知识点:

深拷贝和浅拷贝:

浅拷贝是指只拷贝了引用并没有拷贝内容,b浅拷贝了a,那么a和b的内存地址是一样的,a的值改变那么b的值也会改变

a := temp{
		1,2,
	}

go实现浅拷贝:

//浅拷贝
b := &a

go实现深拷贝

//深拷贝
c:=a

最终测试结果

a的地址为0xc00008a010,值为(1,2)
b的地址为0xc00008a010,值为(1,2)
c的地址为0xc00008a050,值为(1,2)
修改a的值为(3,4)
a的地址为0xc00008a010,值为(3,4)
b的地址为0xc00008a010,值为(3,4)
c的地址为0xc00008a050,值为(1,2)

反向代理

实现原理:客户端发送请求到反向代理服务器,反向代理服务器将请求转发给其他到实现了功能的真正的服务器,从此可以隐藏服务器的真实地址

创建真实服务器:

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)
	server:=http.Server{
		Addr:r.Addr,
		WriteTimeout:3*time.Second,
		Handler:mux,

	}
	go func() {
		log.Fatal(server.ListenAndServe())

	}()


}
func (r *RealServer) HelloHandler(w http.ResponseWriter ,req *http.Request)  {
	//127.0.0.1:8080/abc?daw=1
	//r.Addr = 127.0.0.1:8080
	//req.URL.Path = /abc

	upath:=fmt.Sprintf("客户端的地址为:http://%s%s\n",r.Addr,req.URL.Path)
	io.WriteString(w,upath)
}

func (r *RealServer) ErrorHandler(w http.ResponseWriter ,req *http.Request)  {
	upath:="error handler"
	w.WriteHeader(500)
	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

}

创建反向代理服务器,并绑定127.0.0.1:2003作为代理转发服务器

var(
	provy_addr = "http://127.0.0.1:2003"
	port = "2002"
)

func handler (w http.ResponseWriter,r *http.Request)  {
	//解析代理地址,更改请求体的协议和主机
	proxy,err:=url.Parse(provy_addr)
	r.URL.Scheme=  proxy.Scheme
	r.URL.Host = proxy.Host
	if err!=nil{
		log.Println(err)
		return
	}

	//请求下游
	transport:=http.DefaultTransport
	resp,err:=transport.RoundTrip(r)
	if err!=nil{
		log.Println(err)
		return
	}

	//把下游请求内容返回给上游
	for k,vv:=range resp.Header{
		for _,v:=range vv{
			w.Header().Add(k,v)
		}

	}
	defer resp.Body.Close()
	bufio.NewReader(resp.Body).WriteTo(w)

}


func main() {

	http.HandleFunc("/",handler)

	log.Println("转发代理服务器开启 port "+port)
	err:=http.ListenAndServe(":"+port,nil)
	if err!=nil{
		log.Fatal(err)
	}

}

反向代理修改返回的reponse内容

如果我们在反向代理服务器返回结果时需要修改一些内容时,比如更新时间戳等,那么就需要修改NewSingleHostReverseProxy函数并定义ModifyResponse函数了,在NewSingleHostReverseProxy函数中我们需要在调用httputil.ReverseProxy的时候修改我们的ModifyResponse

其中ModifyResponse的作用是修改拷贝response的body的内容,并修改,最后回写到response.body,最后还需要修改contentLength的值,才能正确解析response

func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
	targetQuery := target.RawQuery
	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host

		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(res *http.Response) error{
		if res.StatusCode !=200{

			oldPayload,err:=ioutil.ReadAll(res.Body)
			if err!=nil{
				return err
			}
			newPayLoad := []byte("hello "+string(oldPayload))

			//回写到res
			res.Body =ioutil.NopCloser(bytes.NewBuffer(newPayLoad))
			//修改contentLength
			res.ContentLength = int64(len(newPayLoad))
			res.Header.Set("Content-Length",fmt.Sprint(len(newPayLoad)))


		}
		return nil

	}


	return &httputil.ReverseProxy{Director: director,ModifyResponse:modifyFunc}
}

简单实现了反向代理服务器,但是没有实现轮询转发的功能,以后的博文会进行改进,实现轮询转发的功能