最近对 Go 语言的反向代理使用得偏多,其实在大概两年前就写过 TCP 层面的代理,而且那时也是用的 Go 语言,不同之处在于之前只是偶尔尝试一下使用,最近是因为工作需要使用的。相比较于 TCP 层面的代理,HTTP 的代理实现起来麻烦事比较多,如果我们仅仅是简单的反向代理,OK,那还好,做个 Host 替换就差不多了。但是,很多时候我们作反向代理,那么需求就比较多样了,例如我们可能希望对代理的响应内容做些改变,也可能希望是 HA 的反向代理。

Go 语言在自身的内置库中就提供了一个很方便的反向代理组件,使用内置的 httputil 的组件我们可以非常快的开发出一个简单的反向代理,稍后我们会看到。但是,一旦与我们前面提到的多样化的需求的时候,就不能简单得写代码了,我们得了解一些组件,然后根据需求对组件进行个性化,从而满足我们的需求。下面,我就从简入繁来看下 Go 语言内置的反向代理库。

内置反向代理

根据 Go 语言官方文档:Package httputil 中描述,我们可以写这么一段简单的代码,这段代码是从 Go 语言的官方文档上抄下来的:

这里我对这段代码解释一下:

  • Line 2-5: 创建了一个简单的服务器,响应一些 URL,内容就是里面的字符串
  • Line 7-12:这才是真正的创建反向代理的代码,这里创建了一个反向代理的 ReverProxy 实体
  • Line 14-24:这些是仿造一个客户端请求前面的反向代理,然后获取输出的响应

执行一下这段代码,应该看到的结果是:

this call was relayed by the reverse proxy

Director

看上去反向代理挺简单的,6 行代码就已经完成了,而且还带错误处理,OK,是时候做点有意思的事情了,不妨我们就做个代理 baidu.com 的反向代理好了,看看效果。为什么是代理 baidu.com,因为它可以进行很浪的操作啊,自行发挥呗:

一个很简单的想法就是这么代理,然后我们尝试运行一下代码就会发现根!本!行!不!通!那怎么办,这个时候就是需要你去思考这中间可能的问题了,或者你该利用一些工具分析一下中间环节哪里不一样。但是,我这里不准备将怎么去发现这个问题,我会告诉你问题就是 HTTP 请求头不对,所以我们该更改一下请求的请求头,所以代码应该修改成这样:

这里有一个关键的地方就是 Line 11,如果你去对比我的这段代码和 Go 语言自带的 defaultDirector 的代码,你也会发现其实就是多了 Line 11 这一行,但是这一行及其关键,很多 Server 可能出于各种考虑,会屏蔽掉 Host 不为自己的请求,而我们作为代理需要将这个补上。

OK,运行这段代码,然后我们就可以通过 localhost:9090 成功访问 baidu.com 了,看上去应该已经成功了。

ModifyResponse

有时候,我这个代理可能会给别人访问,于是乎出于版权等因素,我会做一些小手段,例如这里的响应头:

这个 Server: BWS/1.1 不是很好,我觉得有必要换成我自己的服务器名字:Server:ProjectZoo,于是乎,又有事情可以搞了,要怎么修改这个响应头呢,Go 语言也给我们提供了,那就是 ModifyResponse,来,尝试一下:

在途中这个问题我加了几行代码,然后再访问一遍看看:

一切正如我所期望的,它成了!这其实就是一个简单修改响应的示例,可以做到很强大的功能,但是,这没必要展开说了。需要强调的一点就是,如果需要修改响应体,别忘了同时更新 Header 的 Content-Length,不然,这就是留给自己的一个深坑哦。

Transport

在前面的两个操作里面,我们对 HTTP 请求的反向代理前和后都进行了一番操作,但是,有点不爽的地方在于我们是分别在两个地方进行处理的,这缺乏一些统一性。那么有没有一种方法,可以在一个地方完成这些事情呢,很明显 Go 语言为我们提供了这个一个地方,那就是 Transport,官方文档中是这么描述 Transport 的:

// The transport used to perform proxy requests.

这似乎还有点太简单了,不如看一下 RoundTripper 接口是如何描述的:

RoundTrip executes a single HTTP transaction, returning
a Response for the provided Request.

RoundTrip should not attempt to interpret the response. In
particular, RoundTrip must return err == nil if it obtained
a response, regardless of the response's HTTP status code.
A non-nil err should be reserved for failure to obtain a
response. Similarly, RoundTrip should not attempt to
handle higher-level protocol details such as redirects,
authentication, or cookies.

RoundTrip should not modify the request, except for
consuming and closing the Request's Body. RoundTrip may
read fields of the request in a separate goroutine. Callers
should not mutate the request until the Response's Body has
been closed.

RoundTrip must always close the body, including on errors,
but depending on the implementation may do so in a separate
goroutine even after RoundTrip returns. This means that
callers wanting to reuse the body for subsequent requests
must arrange to wait for the Close call before doing so.
The Request's URL and Header fields must be initialized.

可以看到 Transport 的功能还是很简单的,虽然可以修改 Request 和 Response,但是,就合理性来说是不希望在这里面修改的的,更多的需求还是在于修改 Response 的。既然如此,不妨来看下如何实现 Transport,就以内置的 DefaultTransport 来看吧:

其实 DefaultTransport 自身没什么,无非是设定一些默认参数构建了一下 http.Transport 这个结构体,这里有一个坑就是 Line 2Proxy:ProxyFromEnviroment 会使用系统默认的代理,如果你设置了环境变量 HTTP_PROXY 的话,那么默认是会使用这个代理的,切记切记,这是个坑!

小结

DefaultTransport 这里就不再往下深挖了,因为到这里已经可以满足平时的大部分需求了,再往下其实就涉及到 Go 语言中 HTTP 模块的核心功能了,这些会在后面的文章中讲。在这篇文章中,虽然我只是讲了三个组件,但是,这三个小组件已经很够了,而且,在本文中,我也着重描述了可能出现坑的几个地方,这些地方都值得我们去关注,因为在使用 ReverseProxy 的时候,一般遇到的问题都离不开这几个。

Reference

  1. Package httputil

Go 语言实现 HTTP 层面的反向代理的更多相关文章

  1. GO语言一行代码实现反向代理

    本文,介绍了什么是反向代理,如何用go语言实现反向代理. 至于他的标题, "GO语言一行代码实现反向代理 | Writing a Reverse Proxy in just one line ...

  2. Go语言正/反向代理的姿势

    先重温一下什么叫反向代理,正向代理. 鹅厂二面,nginx回忆录 所谓正向,反向代理取决于代理的是出站请求,还是入站请求. 正向代理: 代理的出站请求, 客户端能感知到代理程序,架构上距离客户端更近. ...

  3. 使用go语言实现简单的反向代理工具激活IntelliJ和PyCharm,持续更新

    最近Jetbrians系列IDE更新至2017.3版本,激活检测机制也变成了动态封禁域名,导致大部分域名激活被屏蔽了,所以找了下资料,根据ilanyu的代码,改了下地址,实现了本地反向代理激活服务器. ...

  4. nginx是一个反向代理的软件

    nginx只是一个反向代理的软件,和语言无关,理论上支持任何Web平台,当然http://Asp.net也不例外,http://51aspx.com就是http://Asp.net开发的,前端暴漏的是 ...

  5. 反向代理代理百度、google

    <VirtualHost _default_:443> # ServerAdmin mail@localhost # DocumentRoot "/var/www/html&qu ...

  6. Ngix 移动端与Pc端 反向代理判断

    如神马搜索和百度(http://www.baidu.com),当用桌面浏览器和移动浏览器访问的结果是不一样的.其中的手段可能有两种: 转载Ngix反向代理判断 服务端直接判断UA输出不同的界面,JAV ...

  7. 基于ssh反向代理实现的远程协助

    本文描述了怎么通过ssh反向代理实现远程协助,并提供了相关代码. 可满足web开启远程协助功能后,维护人员能够通过ssh和http登录客户机器(包括在nat环境下) web开启该功能后,ssh才能登录 ...

  8. aProxy: 带认证授权和权限控制的反向代理

    前段时间很多数据库因为没有做好权限控制暴露在外网被删然后遭勒索的事件,而类似的有些内网的web服务也会被开放到公网并且没有做任何权限控制的,这样也会有一定的风险.所以就决定写篇文章简单介绍一个小工具. ...

  9. Nginx前端设置反向代理,后端Apache如何获取访客的真实IP,结合PHP

    nginx反向代理后,在应用中取得的ip都是反向代理服务器的ip,取得的域名也是反向代理配置的url的域名,解决该问题,需要在nginx反向代理配置中添加一些配置信息,目的将客户端的真实ip和域名传递 ...

随机推荐

  1. BZOJ 1003--[ZJOI2006]物流运输(最短路)

    1003: [ZJOI2006]物流运输 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10034  Solved: 4403 Description ...

  2. FunDA(10)- 用户功能函数模式:User Function Model

    前面我们提过:FunDA就像一个管道(PipeLine).管道内流动着一串数据(Data)或者运算指令(Action).管道的源头就是能产生纯数据的数据源(Source),跟着在管道的中间会有一些节点 ...

  3. Java并发工具类之CountDownLatch

    CountDownLatch允许一个或则多个线程等待其他线程完成操作. 假如我们有这样的需求:我们需要解析一个excel文件中的多个sheet,我们可以考虑使用多线程,每一个线程解析excel中的一个 ...

  4. Python小白学习之路(五)—【类和对象】【列表】【列表相关功能】

    类和对象 (简单的了解一下这个概念,初步有个印象,这个概念很重要,后面会着重讲) 类:具有相同属性和方法的对象的集合: 对象:万物皆对象: 概念很抽象(每当我看不到概念的时候,我就会通过举例来理解) ...

  5. ruby部署之Heroku

    下载安装 :https://devcenter.heroku.com/articles/heroku-cli  (我是windows,所以我下载windows) cmd黑窗口输入: $ heroku ...

  6. 关于class的签名Signature

    举例1: public class Test05<A, B extends java.util.List<String>, C extends InputStream&Ser ...

  7. 关于作用域范围Scope

    举个例子,如下: public class C { public int a = 2; public void test(int b) { int c = 3; for (int d = 3; a & ...

  8. Selenium Web自动化 原理

    文章转自 白月黑羽教Python 原理 说到web应用自动化测试,第一选择就是 Selenium 框架. Selenium 是一个 Web 应用的自动化框架. 通过它,我们可以写出自动化程序像人一样( ...

  9. C#循环读取文件流,按行读取

    public Dictionary<string,string> GetSourceDisksElements(String section) { section = "[&qu ...

  10. mysql查看权限的命令

    mysql查看用户权限的命令 1.这里用来查看用户存储过程: show grants for 用户; eg: show grants for root@'localhost';#这样就会把root用户 ...