Files
sundynix-micro-be/app/gateway/internal/handler/proxy.go
T
2026-04-27 21:23:13 +08:00

96 lines
2.4 KiB
Go

package handler
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
"sundynix-micro-go/app/gateway/internal/config"
"github.com/zeromicro/go-zero/core/logx"
)
// ProxyRouter 反向代理路由器
type ProxyRouter struct {
routes []*route
}
type route struct {
prefix string
proxy *httputil.ReverseProxy
target string
}
// NewProxyRouter 根据配置构建路由表
func NewProxyRouter(upstreams []config.Upstream) *ProxyRouter {
router := &ProxyRouter{}
for _, u := range upstreams {
targetURL, err := url.Parse(u.Target)
if err != nil {
logx.Errorf("解析上游地址失败 [%s]: %v", u.Target, err)
continue
}
target := targetURL // 显式捕获循环变量
proxy := &httputil.ReverseProxy{
Rewrite: func(pr *httputil.ProxyRequest) {
pr.SetXForwarded()
pr.Out.URL.Scheme = target.Scheme
pr.Out.URL.Host = target.Host
pr.Out.Host = target.Host
// 路径透传:前端请求 /api/user/info -> 转发到 user-api 的 /api/user/info
// 不剥前缀,后端API的路由本身就包含 /api/user 前缀
},
}
// 自定义 Transport:超时控制
proxy.Transport = &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 20,
IdleConnTimeout: 90 * time.Second,
}
// 错误处理
prefix := u.Prefix
targetAddr := u.Target
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
logx.Errorf("代理请求失败 [%s%s -> %s]: %v", prefix, r.URL.Path, targetAddr, err)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusBadGateway)
fmt.Fprintf(w, `{"code":502,"msg":"上游服务不可用: %s"}`, prefix)
}
router.routes = append(router.routes, &route{
prefix: u.Prefix,
proxy: proxy,
target: u.Target,
})
logx.Infof("路由注册: %s -> %s", u.Prefix, u.Target)
}
return router
}
// ServeHTTP 根据 URL 前缀匹配路由并转发
func (pr *ProxyRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
for _, rt := range pr.routes {
if strings.HasPrefix(path, rt.prefix) {
logx.Infof("[Gateway] %s %s -> %s", r.Method, path, rt.target)
rt.proxy.ServeHTTP(w, r)
return
}
}
// 没有匹配的路由
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, `{"code":404,"msg":"路由未找到: %s"}`, path)
}