golang 超简单实现反向代理(nginx 端口转发 Proxy)

100行你就可以做到类似nginx带自动更新的端口转发功能

总共就2个文件,一个main(总行数128行),一个配置文件

main:

里面的json解析和log可以忽略

package main

import (
	"github.com/weimingjue/json"
	utils2 "goProxy/utils"
	"goService/utils"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

var (
	projectDir, _         = os.Getwd()
	fileName              = projectDir + "/domain.config"
	readFileTime    int64 = 0  //读取文件的时间
	fileChangedTime int64 = 0  //文件修改时间
	domainData      [][]string //[{***.gq,8080,http://127.0.0.1:8080/}]
	duPeiZhiSuo     sync.Mutex //读配置锁
)

// 获取反向代理域名
func getProxyUrl(reqDomain string) string {
	checkFile()

	for _, dms := range domainData {
		if strings.Index(reqDomain, dms[0]) >= 0 {
			return dms[2]
		}
	}
	return domainData[0][2]
}

//读取配置文件
//域名:端口号,未知域名默认用第一个
func checkFile() {
	nowTime := time.Now().Unix()
	if nowTime-readFileTime < 300 {
		return
	}
	//每5分钟判断文件是否修改
	domainFile, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND, 0)
	info, _ := domainFile.Stat()
	if info.ModTime().Unix() == fileChangedTime {
		return
	}
	duPeiZhiSuo.Lock()
	defer duPeiZhiSuo.Unlock()
	domainFile, _ = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND, 0) //加锁再来一遍,防止重入
	info, _ = domainFile.Stat()
	changedTime := info.ModTime().Unix()
	if changedTime == fileChangedTime {
		return
	}

	//文件改变

	//重置数据
	readFileTime = nowTime
	fileChangedTime = changedTime
	domainData = [][]string{}

	bytes, _ := ioutil.ReadFile(fileName)
	split := strings.Split(string(bytes), "\n")

	for _, domainInfo := range split {
		dLen := len(domainInfo)
		if dLen < 8 || dLen > 20 { //忽略错误信息
			continue
		}
		domainItems := strings.Split(domainInfo, ":")
		if len(domainItems) != 2 || len(domainItems[0]) < 3 || len(domainItems[1]) < 2 {
			continue
		}
		if utils.EndWidth(domainItems[1], "/") {
			domainItems = append(domainItems, "http://127.0.0.1:"+domainItems[1])
		} else {
			domainItems = append(domainItems, "http://127.0.0.1:"+domainItems[1]+"/")
		}
		domainData = append(domainData, domainItems)
	}

	domainSt, _ := json.Marshal(domainData)
	utils2.MyLogProxyI("配置已修改:" + string(domainSt))
}

//获取主机名
func getHost(req *http.Request) string {
	if req.Host != "" {
		if hostPart, _, err := net.SplitHostPort(req.Host); err == nil {
			return hostPart
		}
		return req.Host
	}
	return "localhost"
}

func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) {
	host := getHost(req)
	proxyUrl := getProxyUrl(host)
	url2, _ := url.Parse(proxyUrl)
	utils2.MyLogProxyI("请求域名:" + host + ",转到:" + proxyUrl)

	// create the reverse proxy
	proxy := httputil.NewSingleHostReverseProxy(url2)

	// Update the headers to allow for SSL redirection
	req.URL.Host = url2.Host
	req.URL.Scheme = url2.Scheme
	req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
	req.Host = url2.Host

	// Note that ServeHttp is non blocking and uses a go routine under the hood
	proxy.ServeHTTP(res, req)
}

func main() {
	http.HandleFunc("/", handleRequestAndRedirect)
	if err := http.ListenAndServe(":80", nil); err != nil {
		utils.MyLogE("Proxy监听80端口错误:" + err.Error())
		panic(err)
	}
}

domain.config:

***为自己的域名,":"后面是需要转发的端口,不用写http://,任何地方都不能有空格

wang.gq:8080
***.aa:8081/

代码写的是相对目录请到当前目录执行"go run main.go",愉快的转发从现在开始

参考资料(抄的):https://studygolang.com/articles/14246

你可能感兴趣的:(go)