golang实现正/反向代理服务

筑梦攻城狮 · · 1095 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

1 概念

1.1 正向代理

一种客户端代理技术,用于帮助客户端访问无法直接访问的网络资源,并隐藏客户端IP,常见的场景有***、浏览器HTTP代理

1.2 反向代理

一种服务端代理技术,用于隐藏真实服务端节点,并实现负载均衡、缓存、安全校验、协议转换等,常见的有LVS、nginx

2 实践

2.1 实现一个正向代理服务

2.1.1 基本思路
  • 代理接收客户端请求,复制该请求对象,并根据实际需要配置请求参数
  • 构造新的请求,发送到服务端,并获取服务端的响应内容
  • 接收到响应内容后返回给客户端
2.1.2 具体实现
package main

import (
    "fmt"
    "io"
    "log"
    "net"
    "net/http"
    "strings"
)

type proxy struct {}

func (p *proxy)ServeHTTP(w http.ResponseWriter,r *http.Request){
    fmt.Printf("Received request: %s %s %s\n",r.Method,r.Host,r.RemoteAddr)
    transport := http.DefaultTransport

    // 浅拷贝一个request 对象,避免后续修影响了源对象
    req := new(http.Request)
    *req = *r

    // 设置X-Forward-For 头部
    if clientIp,_,err := net.SplitHostPort(r.RemoteAddr);err ==nil{
        if prior,ok := req.Header["X-Forward-For"];ok{
            clientIp = strings.Join(prior,", ") + ", " + clientIp
        }
        req.Header.Set("X-Forward-For",clientIp)
    }

    // 构造新请求
    response,err:=transport.RoundTrip(req)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    // 获取响应数据并返回
    for k,v := range response.Header{
        for _,v1 := range v{
            w.Header().Add(k,v1)
        }
    }
    w.WriteHeader(response.StatusCode)
    io.Copy(w,response.Body)
    response.Body.Close()

}

func main() {
    //mux := http.NewServeMux()
    server := &http.Server{
        Addr: ":9090",
        Handler: &proxy{},
    }
    if err:=server.ListenAndServe();err != nil{
        log.Fatal("Http proxy server start failed.")
    }
}

2.2 Go实现一个反向代理服务

2.2.1 基本思路
2.2.2 具体实现

Http real Server 实现:监听8081和8082端口,返回请求的URL

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

type realServer struct {
    Addr string
}

func (rs *realServer) HelloHandler(w http.ResponseWriter,r *http.Request){
    reqUrl := fmt.Sprintf("http://%s%s\n",rs.Addr,r.RequestURI)
    w.Write([]byte(reqUrl))
}

func (rs *realServer) Run(){
    fmt.Println("Http server tart to serve at :",rs.Addr)
    mux := http.NewServeMux()
    mux.HandleFunc("/",rs.HelloHandler)
    server := &http.Server{
        Addr: rs.Addr,
        Handler: mux,
        WriteTimeout: time.Second * 3,
    }
    go func(){
        if err := server.ListenAndServe();err != nil{
            log.Fatal("Start http server failed,err:",err)
        }
    }()
}

func main() {
    rs1 := &realServer{Addr:"127.0.0.1:8081"}
    rs2 := &realServer{Addr:"127.0.0.1:8082"}

    go rs1.Run()
    go rs2.Run()

    doneCh := make(chan os.Signal)
    signal.Notify(doneCh,syscall.SIGINT,syscall.SIGTERM)
    <- doneCh
}

模拟请求

// output
$ curl http://127.0.0.1:8081/hellotest?id=1 -s
http://127.0.0.1:8081/hellotest?id=1

$ curl http://127.0.0.1:8082/hellotest?id=2 -s
http://127.0.0.1:8082/hellotest?id=2

反向代理服务端实现

package main

import (
    "bufio"
    "fmt"
    "log"
    "net/http"
    "net/url"
    "time"
)
var serverPort = "8888"

// 为了测试,简单的通过当前时间戳取余的方式模拟随机访问后端rs
func GetRandServer()string{
    ports := []string{"8081","8082"}
    n := time.Now().Unix() % 2
    return ports[n]
}

func handler(w http.ResponseWriter,r *http.Request){
  // 解析并修改代理服务
    port := GetRandServer()
    proxyAddr := "http://127.0.0.1:" + port
    proxy ,err := url.Parse(proxyAddr)
    if err != nil{
        log.Println(err)
        return
    }
    r.URL.Scheme = proxy.Scheme
    r.URL.Host = proxy.Host

    // 代理请求
    transport := http.DefaultTransport
    resp ,err := transport.RoundTrip(r)
    if err != nil{
        log.Println(err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    // 将响应结果返回
    for key,value := range resp.Header{
        for _,v := range value{
            w.Header().Add(key,v)
        }
    }
    defer resp.Body.Close()
    bufio.NewReader(resp.Body).WriteTo(w)
}

func main() {
    http.HandleFunc("/",handler)
    fmt.Println("Http reverse proxy server start at : 127.0.0.1:",serverPort)
    if err := http.ListenAndServe(":"+serverPort,nil);err != nil{
        log.Fatal("Start server failed,err:",err)
    }
}

测试

$ for i in {0..9};do curl http://127.0.0.1:8888/reversetest?id=111 -s;done
http://127.0.0.1:8082/reversetest?id=111
http://127.0.0.1:8082/reversetest?id=111
http://127.0.0.1:8082/reversetest?id=111
http://127.0.0.1:8082/reversetest?id=111
http://127.0.0.1:8081/reversetest?id=111
http://127.0.0.1:8081/reversetest?id=111
http://127.0.0.1:8081/reversetest?id=111
http://127.0.0.1:8081/reversetest?id=111
http://127.0.0.1:8081/reversetest?id=111
http://127.0.0.1:8082/reversetest?id=111

有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:筑梦攻城狮

查看原文:golang实现正/反向代理服务

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1095 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传