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

代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端。


Table of Contents


反向代理

反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个服务器。

实现逻辑

根据代理的描述一共分成几个步骤:

  1. 代理接收到客户端的请求,复制了原来的请求对象
  2. 根据一些规则,修改新请求的请求指向
  3. 把新请求发送到根据服务器端,并接收到服务器端返回的响应
  4. 将上一步的响应根据需求处理一下,然后返回给客户端

Go 语言实现

原生代码

因为要接收并转发 http 请求,所以要实现 http.Handler

type OriginReverseProxy struct {
servers []*url.URL
} func NewOriginReverseProxy(targets []*url.URL) *OriginReverseProxy {
return &OriginReverseProxy{
servers: targets,
}
} // 实现 http.Handler, 用于接收所有的请求
func (proxy *OriginReverseProxy) ServeHTTP(rw http.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。处理响应
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 clone(req *http.Request) *http.Request {
r2 := new(http.Request)
*r2 = *req
r2.URL = cloneURL(req.URL)
if req.Header != nil {
r2.Header = req.Header.Clone()
}
if req.Trailer != nil {
r2.Trailer = req.Trailer.Clone()
}
if s := 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)
return r2
} func cloneURLValues(v url.Values) url.Values {
if v == nil {
return nil
}
return url.Values(http.Header(v).Clone())
} func cloneURL(u *url.URL) *url.URL {
if u == nil {
return nil
}
u2 := new(url.URL)
*u2 = *u
if u.User != nil {
u2.User = new(url.Userinfo)
*u2.User = *u.User
}
return u2
}

测试


// 先用 gin 起一个 web 项目,方便转发
func TestGin(t *testing.T) {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run(":9091") // listen and serve on 0.0.0.0:9091
} func main() {
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 来处理异常

type ReverseProxy struct {
// Director must be a function which modifies
// the request into a new request to be sent
// using Transport. Its response is then copied
// back to the original client unmodified.
// Director must not access the provided Request
// after returning.
Director func(*http.Request) Transport http.RoundTripper FlushInterval time.Duration ErrorLog *log.Logger BufferPool BufferPool ModifyResponse func(*http.Response) error ErrorHandler func(http.ResponseWriter, *http.Request, error)
}

这里我们通过自定义 Director 来修改请求地址


func NewMultipleHostsReverseProxy(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 的项目还是要启动的,这里不在赘叙

func TestMultipleHostsReverseProxy(t *testing.T) {
proxy := proxy.NewMultipleHostsReverseProxy([]*url.URL{
{
Scheme: "http",
Host: "localhost:9091",
},
})
http.ListenAndServe(":9090", proxy)
}

接入 consul 实现动态代理

在前面的一篇文章中讲了如何用 Go 实现 Consul 的服务发现, 如果要结合 consul 实现动态代理,需要考虑如何将请求的地址和对应的服务对应上。这里需要在原理的基础上加上一下功能:

  1. 根据请求地址找到对应的服务
  2. 根据服务找到对应的示例

针对第一步先实现最简单的,就以 请求地址开头 为规则

type LoadBalanceRoute interface {
ObtainInstance(path string) *url.URL
} type Route struct {
Path string
ServiceName string
} type DiscoveryLoadBalanceRoute struct { DiscoveryClient DiscoveryClient Routes []Route } func (d DiscoveryLoadBalanceRoute) ObtainInstance(path string) *url.URL {
for _, route := range d.Routes {
if strings.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(),
}
}
}
return nil
} func NewLoadBalanceReverseProxy(lb LoadBalanceRoute) *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}
}

测试

func main() {
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 | Go 结合 Consul 实现动态反向代理的更多相关文章

  1. 基于nginx + lua实现的反向代理动态更新

    大家都知道,nginx是当前应用非常广泛的web服务器,热度因为他的高并发高性能高可靠性,且轻量级!牛逼的不行,不多说这些. 今天要介绍的是,如何基于nginx和lua脚本,也就是在openresty ...

  2. Tomcat安装部署和安全加固优化以及反向代理应用

    1.Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同 ...

  3. Spring boot2X集成zuul与consul实现负载均衡和反向代理

    zuul 是netflix开源的一个API Gateway 服务器 所有从设备或网站来的请求都会经过Zuul到达后端的Netflix应用程序. 作为一个边界性质的应用程序,Zuul提供了动态路由.监控 ...

  4. 使用Nginx反向代理和内容替换模块实现网页内容动态替换功能

    2016年11月21日 10:30:00 xian_02 阅读数:10943   Nginx是一款轻量级高性能服务器软件,虽然轻量,但功能非常强大,可用于提供WEB服务.反向代理.负载均衡.缓存服务. ...

  5. nginx详解反向代理、负载均衡、LNMP架构上线动态网站(week4_day1_part1)-技术流ken

    nginx介绍 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理 ...

  6. 002-nginx-在 nginx 反向代理中使用域名,配置动态域名解析

    一.概述 代理(proxy),即中间人,它代替客户端发送请求给服务器,收到响应后再转给客户端.通常意义上的代理是从用户的角度讲的,用户通过某个代理可以访问多个网站,这个代理是靠近用户的,比如某些公司可 ...

  7. IIS 7 反向代理 URL重写 转发动态请求

    一.反向代理是什么 有一篇文章说的挺好的 Nginx 反向代理.负载均衡.页面缓存.URL重写及读写分离详解 http://www.server110.com/nginx/201402/5534.ht ...

  8. nginx详解反向代理、负载均衡、LNMP架构上线动态网站

    简介 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器.N ...

  9. nginx详解反向代理,负载均衡,LNMP架构上线动态网站

    1.nginx介绍 nginx.org Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/ ...

随机推荐

  1. spring源码学习笔记之容器的基本实现(一)

    前言 最近学习了<<Spring源码深度解析>>受益匪浅,本博客是对学习内容的一个总结.分享,方便日后自己复习或与一同学习的小伙伴一起探讨之用. 建议与源码配合使用,效果更嘉, ...

  2. 浅谈Winform控件开发(一):使用GDI+美化基础窗口

    写在前面: 本系列随笔将作为我对于winform控件开发的心得总结,方便对一些读者在GDI+.winform等技术方面进行一个入门级的讲解,抛砖引玉. 别问为什么不用WPF,为什么不用QT.问就是懒, ...

  3. C++隐式推导-auto关键词

    总述 C++中有一个关键字,它不进行显式声明,而进行隐式推导,auto可以在声明变量时根据变量初始值的类型自动为此变量选择匹配的类型.C++语言类似的关键字还有decltype. 如何评价 C++ 1 ...

  4. poj 3278 Catch That Cow(记忆化广度优先搜索)

    题意: 0到N的数轴上,每次可以选择移动到x-1,x+1,2*x,问从n移动到k的最少步数. 思路: 同时遍历三种可能并记忆化入队即可. Tips: n大于等于k时最短步数为n-k. 在移动的过程中可 ...

  5. hdu3436 Queue-jumpers(Splay)

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission ...

  6. 【noi 2.6_2421】Exchange Rates(DP)

    题意:起始有1000元美元,给出N天美元与加拿大元的汇率.问N天内可以不停的兑换,每次兑换需要收取3%的手续费,问可以得到的最大的美元数. 解法:直接用2个变量存第 i 天时手中是美元和加拿大元的最大 ...

  7. CodeForces - 915E 动态开点线段树

    题目 晚上有n个亮着的灯泡,标号从1到n. 现在存在2种操作,如下: 操作1,关掉标号 [l,r] 区间的灯 操作2,打开标号 [l,r] 区间的灯 下面有q次询问,每次询问执行其中一种操作,询问格式 ...

  8. PowerShell随笔2---初始命令

    PowerShell便捷之处 PowerShell中兼容运行cmd的命令,比如 ipconfig.ping命令等 PowerShell的命令更友好,可读性更强.比如停止一个服务 CMD命令:sc st ...

  9. 部署 WordPress 和 Wecenter

    目录 基本环境部署(LNMP) 安装 Nginx 安装 PHP7.1 安装 Mariadb 安装 NFS 部署 NFS 服务端 部署 NFS 客户端 部署 WordPress 首台服务器 环境部署 数 ...

  10. C/C++程序内存的各种变量存储区域和各个区域详解

    转自 https://blog.csdn.net/jirryzhang/article/details/79518408 C语言在内存中一共分为如下几个区域,分别是: 1. 内存栈区: 存放局部变量名 ...