golang实现正/反向代理服务
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang实现正/反向代理服务相关的知识,希望对你有一定的参考价值。
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
",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
",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
以上是关于golang实现正/反向代理服务的主要内容,如果未能解决你的问题,请参考以下文章