半年前,为了解决测试环境多套应用灵活切换来接受外部系统回调问题,利用 nginx 的 dynamic_upstream 插件 + 封装 httpAPI 来随时更新 upstream 的方式解决(https://testerhome.com/topics/32676)
但几月前,多套测试环境切换已经改成使用公共代理(nohosts)+ 特殊请求头(标记去请求哪套环境或开发的后端服务)的方式了,原方式已经失效。
为了不修改原回调转发器的功能,给每个测试环境增加了 1 个 nginx,将每个 nginx 的 ip 提供给回调转发器,并且每个 niginx 再二次转发时设置上对应的特殊请求头。
一开始的临时方案如下:
但是这个临时方案增加系统的复杂度,排查错误更加麻烦,需要检查更多的节点。
为了简化结构,google 找了一圈工具,没找到合适、轻量且满足以下要求的工具:
想了想,还是自写工具来代替 nginx,因为原封装的 http api 服务是 go 写的,只要在其里面另 go 一个协程启动反向代理服务("net/http/httputil"下的 NewSingleHostReverseProxy)出来即可。
利用 http api 修改全局的转发配置,反向代理每次按照当前配置转发和设置请求头即可满足。
http-api 服务是主程(使用的 iris 框架只是图方便,后续加上权限控制也容易),不赘述。
反向代理服务简单说明
ProxyServ: ':82'
Confs:
- Name: pay
Target: http://127.0.0.1:80
Routes:
- Name: stripe-xxx
Host: pay.xxx.com
Path: '(/xxx-com/callback)/(stripexxx)'
RePath: '{$1}/{$2}'
ReqHeaders:
- Name: Environment-Label
Value: trunk
- Name: payoneer
Host: pay.xxx.com
Path: '(/xxx-com/callback)/payoneer'
RePath: '{$1}/payoneer'
ReqHeaders:
- Name: Environment-Label
Value: trunk
- Name: dd_social_pay
Target: http://127.0.0.1:80
Routes:
- Name: stripe-xxx-dd
Host: pay.xxx.com
Path: '/callback/stripexxx/connect'
RePath: '/callback/connect'
ReqHeaders:
- Name: Environment-Label
Value: trunk
对应 go 中 struct:
// 请求头设置
type ReqHeader struct {
Name string `yaml:"Name"`
Value string `yaml:"Value"`
}
// 路由设置
type Route struct {
Name string `yaml:"Name"`
Host string `yaml:"Host"`
Path string `yaml:"Path"`
RePath string `yaml:"RePath"`
ReqHeaders []*ReqHeader `yaml:"ReqHeaders"`
}
// 单个反向设置
type SigleReverse struct {
Name string `yaml:"Name"`
Target string `yaml:"Target"`
Routes []*Route `yaml:"Routes"`
}
// 简单反向代理设置
type ReverseConf struct {
ProxyServ string `yaml:"ProxyServ"`
Confs []*SigleReverse `yaml:"Confs"`
}
proxy.Director = func(req *http.Request) {
//sr是全局配置中一个服务配置(SigleReverse),Target为其转发目标
target, err := url.Parse(strings.TrimSpace(sr.Target))
if err != nil {
log.Printf("转发的URL有误:%s", sr.Target)
}
targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", "")
}
xff := strings.TrimSpace(req.Header.Get("X-Forwarded-For"))
if xff != "" {
xff = xff + "," + strings.Split(req.RemoteAddr, ":")[0]
} else {
xff = strings.Split(req.RemoteAddr, ":")[0]
}
req.Header.Set("X-Forwarded-For", xff)
for _, srh := range rt.ReqHeaders {
req.Header.Set(srh.Name, srh.Value)
}
if strings.TrimSpace(rt.Host) != "" {
req.Host = rt.Host
}
req.URL.Path = newPath
log.Printf("url:%s, host:%s, xff:%s\n", req.URL.String(), req.Host, req.Header.Get("X-Forwarded-For"))
}
proxy.ServeHTTP(w, r)
github:
https://github.com/mao303mao/go_dynamic_reverse_proxy