Go | Go 结合 Consul 实现动态反向代理
本文内容纲要:
-Go结合Consul实现动态反向代理
-反向代理
-实现逻辑
-Go语言实现
-原生代码
-httputil.ReverseProxy工具实现
-接入consul实现动态代理
-参考
Go结合Consul实现动态反向代理
代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端。
TableofContents
-
反向代理
-
实现逻辑
-
Go语言实现
- 原生代码
- httputil.ReverseProxy工具实现
-
接入consul实现动态代理
-
参考
反向代理
反向代理(ReverseProxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
实现逻辑
根据代理的描述一共分成几个步骤:
- 代理接收到客户端的请求,复制了原来的请求对象
- 根据一些规则,修改新请求的请求指向
- 把新请求发送到根据服务器端,并接收到服务器端返回的响应
- 将上一步的响应根据需求处理一下,然后返回给客户端
Go语言实现
原生代码
因为要接收并转发http请求,所以要实现http.Handler
typeOriginReverseProxystruct{
servers[]*url.URL
}
funcNewOriginReverseProxy(targets[]*url.URL)*OriginReverseProxy{
return&OriginReverseProxy{
servers:targets,
}
}
//实现http.Handler,用于接收所有的请求
func(proxy*OriginReverseProxy)ServeHTTP(rwhttp.ResponseWriter,req*http.Request){
//1.复制了原来的请求对象
r2:=clone(req)
//2.修改请求的地址,换为对应的服务器地址
target:=proxy.servers[rand.Int()%len(proxy.servers)]
r2.URL.Scheme=target.Scheme
r2.URL.Host=target.Host
//3.发送复制的新请求
transport:=http.DefaultTransport
res,err:=transport.RoundTrip(r2)
//4。处理响应
iferr!=nil{
rw.WriteHeader(http.StatusBadGateway)
return
}
forkey,value:=rangeres.Header{
for_,v:=rangevalue{
rw.Header().Add(key,v)
}
}
rw.WriteHeader(res.StatusCode)
io.Copy(rw,res.Body)
res.Body.Close()
}
//复制原请求,生成新的请求
funcclone(req*http.Request)*http.Request{
r2:=new(http.Request)
*r2=*req
r2.URL=cloneURL(req.URL)
ifreq.Header!=nil{
r2.Header=req.Header.Clone()
}
ifreq.Trailer!=nil{
r2.Trailer=req.Trailer.Clone()
}
ifs:=req.TransferEncoding;s!=nil{
s2:=make([]string,len(s))
copy(s2,s)
r2.TransferEncoding=s2
}
r2.Form=cloneURLValues(req.Form)
r2.PostForm=cloneURLValues(req.PostForm)
returnr2
}
funccloneURLValues(vurl.Values)url.Values{
ifv==nil{
returnnil
}
returnurl.Values(http.Header(v).Clone())
}
funccloneURL(u*url.URL)*url.URL{
ifu==nil{
returnnil
}
u2:=new(url.URL)
*u2=*u
ifu.User!=nil{
u2.User=new(url.Userinfo)
*u2.User=*u.User
}
returnu2
}
测试
//先用gin起一个web项目,方便转发
funcTestGin(t*testing.T){
r:=gin.Default()
r.GET("/ping",func(c*gin.Context){
c.JSON(200,gin.H{
"message":"pong",
})
})
r.Run(":9091")//listenandserveon0.0.0.0:9091
}
funcmain(){
proxy:=proxy.NewOriginReverseProxy([]*url.URL{
{
Scheme:"http",
Host:"localhost:9091",
},
})
http.ListenAndServe(":19090",proxy)
}
请求http://127.0.0.1:19090/ping
返回{"message":"pong"}
httputil.ReverseProxy工具实现
在上面的例子中,我们自己实现了请求的接收、复制、转发和处理。自己写的代码还算凑合吧。
从上面的示例中,其实我们主要关心的是第二步:修改请求的地址,换为对应的服务器地址,其余的逻辑都是通用的,还好官方已经帮我们处理了重复逻辑,那我们看看官方是怎么实现的
在httputil.ReverseProxy
源码中,可以看出,通过自定义Director
方法就可以在原请求复制后,新请求转发出之前对复制出的新请求进行修改,这里就是我们真正改动的地方,当然如果有其他定制需求,可以通过自定义ModifyResponse
实现对响应的修改,自定义ErrorHandler
来处理异常
typeReverseProxystruct{
//Directormustbeafunctionwhichmodifies
//therequestintoanewrequesttobesent
//usingTransport.Itsresponseisthencopied
//backtotheoriginalclientunmodified.
//DirectormustnotaccesstheprovidedRequest
//afterreturning.
Directorfunc(*http.Request)
Transporthttp.RoundTripper
FlushIntervaltime.Duration
ErrorLog*log.Logger
BufferPoolBufferPool
ModifyResponsefunc(*http.Response)error
ErrorHandlerfunc(http.ResponseWriter,*http.Request,error)
}
这里我们通过自定义Director
来修改请求地址
funcNewMultipleHostsReverseProxy(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}
}
测试
gin的项目还是要启动的,这里不在赘叙
funcTestMultipleHostsReverseProxy(t*testing.T){
proxy:=proxy.NewMultipleHostsReverseProxy([]*url.URL{
{
Scheme:"http",
Host:"localhost:9091",
},
})
http.ListenAndServe(":9090",proxy)
}
接入consul实现动态代理
在前面的一篇文章中讲了如何用Go实现Consul的服务发现,如果要结合consul实现动态代理,需要考虑如何将请求的地址和对应的服务对应上。这里需要在原理的基础上加上一下功能:
- 根据请求地址找到对应的服务
- 根据服务找到对应的示例
针对第一步先实现最简单的,就以请求地址开头
为规则
typeLoadBalanceRouteinterface{
ObtainInstance(pathstring)*url.URL
}
typeRoutestruct{
Pathstring
ServiceNamestring
}
typeDiscoveryLoadBalanceRoutestruct{
DiscoveryClientDiscoveryClient
Routes[]Route
}
func(dDiscoveryLoadBalanceRoute)ObtainInstance(pathstring)*url.URL{
for_,route:=ranged.Routes{
ifstrings.Index(path,route.Path)==0{
instances,_:=d.DiscoveryClient.GetInstances(route.ServiceName)
instance:=instances[rand.Int()%len(instances)]
scheme:="http"
return&url.URL{
Scheme:scheme,
Host:instance.GetHost(),
}
}
}
returnnil
}
funcNewLoadBalanceReverseProxy(lbLoadBalanceRoute)*httputil.ReverseProxy{
director:=func(req*http.Request){
target:=lb.ObtainInstance(req.URL.Path)
req.URL.Scheme=target.Scheme
req.URL.Host=target.Host
}
return&httputil.ReverseProxy{Director:director}
}
测试
funcmain(){
registry,_:=proxy.NewConsulServiceRegistry("127.0.0.1",8500,"")
reverseProxy:=proxy.NewLoadBalanceReverseProxy(&proxy.DiscoveryLoadBalanceRoute{
DiscoveryClient:registry,
Routes:[]proxy.Route{
{
Path:"abc",
ServiceName:"abc",
},
},
})
http.ListenAndServe(":19090",reverseProxy)
}
本文内容总结:Go结合Consul实现动态反向代理,反向代理,实现逻辑,Go语言实现,原生代码,httputil.ReverseProxy工具实现,接入consul实现动态代理
原文链接:https://www.cnblogs.com/zyndev/p/14454891.html