首页 文章资讯内容详情

Go | Go 结合 Consul 实现动态反向代理

2026-06-01 4 花语

本文内容纲要:

-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