一. 代理基础概念了解
什么是正向代理
- 是一种客户端的代理技术缩写为"forward_proxy",帮助客户端访问无法访问的服务资源,可以隐藏用户的真实ip,如浏览器web代理、vpn等
- 大概步骤:
- 监听中的代理服务器在接收到客户端的请求后,会创建一个上游的tcp连接,通过回调方法,复制原请求对象,并根据其中的数据配置新的请求中的各种参数
- 把新请求发送到真实的服务器,并接收到服务器端的返回
- 代理服务器对响应做一些处理后,返回给客户端
什么是反向代理
- 是一种服务端的代理技术,帮助服务器做负载均衡、缓存、提供安全校验等,可以隐藏服务器的真实ip。如LVS技术、Nginx Proxy_pass等
- 大概步骤:
- 代理接收客户端请求,更改请求结构体信息
- 通过一定的负载均衡算法获取下游服务器地址
- 把请求发送到下游服务器,并获取返回内容
- 对返回内容做一些处理,返回给客户端
二. 基于原生HTTP实现代理
简单正向代理示例
- 实现步骤
- 自定义结构体,实现ServeHTTP(),编写处理器
- ServeHTTP()中编写正向代理需要处理的业务逻辑
- 将自定义的处理器与对应的接口绑定,注册路由(此处示例中绑定的接口为"/")
- 编写main方法监听服务端口,启动服务
- 由于自定义处理器路由绑定的接口为"/",所以访问当前服务的所有接口都会被代理到
import (
"fmt"
"io"
"net"
"net/http"
"strings"
)
//1.创建结构体
type Proxy struct{}
//2.结构体实现ServerHTTP()函数,将结构体变为Header
func (p *Proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
fmt.Printf("接收请求 method: %s, host: %s, remoteAddr: %s", req.Method, req.Host, req.RemoteAddr)
//1.获取一个默认的连接池
transport := http.DefaultTransport
//2.浅拷贝获取到请求数据
outReq := new(http.Request)
*outReq = *req
//3.获取发送请求的ip地址,放到"X-Forwarded-For"请求头中
if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); nil == err {
if prior, ok := outReq.Header["X-Forwarded-For"]; ok {
//X-Forwarded-For多个时采用","拼接
clientIp = strings.Join(prior, ",") + "," + clientIp
}
outReq.Header.Set("X-Forwarded-For", clientIp)
}
//4.请求下游服务,获取到下游服务响应
resp, err := transport.RoundTrip(outReq)
if nil != err {
//如果异常响应异常
rw.WriteHeader(http.StatusBadGateway)
return
}
//5.封装响应头
for key, val := range resp.Header {
for _, v := range val {
rw.Header().Add(key, v)
}
}
//6.封装正常响应
rw.WriteHeader(resp.StatusCode)
//将resp中拿到的响应数据复制到rw中
io.Copy(rw, resp.Body)
//关闭响应资源
resp.Body.Close()
}
func main() {
//注册上面自定义的Proxy{}处理器,绑定"/"所有接口路径
//意思是只要请求当前服务"http:127.0.0.1:8080/*"下的所有接口都会被
//Proxy下实现的ServeHTTP()执行代理
http.Handle("/", &Proxy{})
http.ListenAndServe("0.0.0.0:8080", nil)
}
简单反向代理示例
- 实现步骤
- 明确需要调用的真实服务地址
- 实现ServeHTTP(),编写处理器,处理器中拿到真实服务地址,改写请求协议,主机地址等
- 监听指定端口,启动当前服务
import (
"bufio"
"log"
"net/http"
"net/url"
)
//1.代理后真实访问的服务地址
var (
proxy_addr = "http://127.0.0.1:2003"
)
//2.实现ServeHTTP,编写处理器
func handler(w http.ResponseWriter, r *http.Request) {
//1.通过url.Parse()解析代理地址,拿到真实服务地址
proxy, err := url.Parse(proxy_addr)
//2.更改请求体的协议和主机
r.URL.Scheme = proxy.Scheme
r.URL.Host = proxy.Host
//3.获取一个默认的连接池
transport := http.DefaultTransport
//4.通过连接池的RoundTrip()请求下游服务,并拿到响应结果
resp, err := transport.RoundTrip(r)
//关闭资源
defer resp.Body.Close()
if err != nil {
log.Print(err)
return
}
//5.封装下游服务的响应头
for k, vv := range resp.Header {
for _, v := range vv {
w.Header().Add(k, v)
}
}
//6.下游服务的响应输出给当前服务的调用方
bufio.NewReader(resp.Body).WriteTo(w)
}
func main() {
//注册路由,处理器为上方自定义处理器
http.HandleFunc("/", handler)
//监听端口启动服务
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}
上方代理中可能存在的问题
- 上方才有原生HTTP实现正反向代理,可能存在一下问题
- 没有错误回调及错误日志等处理
- 无法更改代理后返回的内容
- 没有负载均衡
- 没有url重写
- 没有熔断限流,降级,数据统计等功能
- 解决以上问题,golang提供了ReverseProxy实现http代理