fusenapi/proxyserver/main.go

337 lines
7.9 KiB
Go
Raw Normal View History

2023-07-11 05:07:44 +00:00
package main
import (
"fmt"
"io"
2023-07-19 02:59:13 +00:00
"io/ioutil"
2023-07-11 05:07:44 +00:00
"log"
"net"
"net/http"
"net/http/httputil"
2023-07-11 05:07:44 +00:00
"net/url"
2023-07-19 02:59:13 +00:00
"os"
"path/filepath"
"strings"
2023-07-12 06:11:04 +00:00
"sync"
2023-07-11 05:07:44 +00:00
"time"
2023-07-19 02:59:13 +00:00
"gopkg.in/yaml.v2"
2023-07-11 05:07:44 +00:00
)
2023-07-17 11:43:43 +00:00
// Backend结构体
2023-07-11 05:07:44 +00:00
var Backends []*Backend
2023-07-17 11:43:43 +00:00
// 设置跨域请求
2023-07-11 10:48:22 +00:00
func SetCors(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
// 如果请求方法为 OPTIONS直接返回 200 状态码
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
}
2023-07-17 11:43:43 +00:00
// 存储路径的并发安全的Map
2023-07-12 06:11:04 +00:00
var pathdict sync.Map = sync.Map{}
2023-07-11 05:07:44 +00:00
func main() {
2023-07-17 11:43:43 +00:00
// 将静态资源路径存储到pathdict
2023-07-12 06:11:04 +00:00
pathdict.Store("/css", true)
pathdict.Store("/fonts", true)
pathdict.Store("/img", true)
pathdict.Store("/js", true)
pathdict.Store("/svg", true)
pathdict.Store("/favicon.ico", true)
2023-07-17 11:43:43 +00:00
rootDir := "../server" // 更改为你的根目录
2023-07-12 06:23:10 +00:00
vueBuild := "/opt/fusenpack-vue-created"
apiURL, err := url.Parse("http://localhost:9900")
if err != nil {
panic(err)
}
2023-07-11 05:07:44 +00:00
mux := http.NewServeMux()
2023-07-17 11:43:43 +00:00
// 获取并解析服务信息
2023-07-11 05:07:44 +00:00
results := GetZeroInfo(rootDir)
var allRoutes map[string]bool = make(map[string]bool)
2023-07-11 05:07:44 +00:00
for _, result := range results {
fmt.Printf("FolderName: %s, Host: %s, Port: %d, PrefixRoute: %v\n", result.FolderName, result.Host, result.Port, result.PrefixRoute)
var routes []string
for k := range result.PrefixRoute {
routes = append(routes, k)
allRoutes[k] = true
2023-07-11 05:07:44 +00:00
}
2023-07-17 11:43:43 +00:00
// 根据获取的服务信息创建后端服务
2023-07-11 05:07:44 +00:00
Backends = append(Backends,
NewBackend(mux,
fmt.Sprintf("http://%s:%d", result.Host, result.Port),
routes...))
}
2023-07-17 11:43:43 +00:00
// 定义用于服务Vue dist文件夹的静态文件服务器
fs := http.FileServer(http.Dir(vueBuild))
2023-07-11 10:48:22 +00:00
indexHtmlPath := vueBuild + "/index.html"
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/api/") {
2023-07-17 11:43:43 +00:00
// 对/api开头的请求进行反向代理
proxy := httputil.NewSingleHostReverseProxy(apiURL)
proxy.ServeHTTP(w, r)
return
} else {
2023-07-17 11:43:43 +00:00
// 根据请求路径判断是服务静态文件或者是返回index.html
2023-07-12 06:11:04 +00:00
idx := strings.Index(r.URL.Path[1:], "/")
var prefix string
if idx != -1 {
prefix = r.URL.Path[:idx+1]
} else {
2023-07-12 06:11:04 +00:00
prefix = r.URL.Path
}
if _, ok := pathdict.Load(prefix); ok {
fs.ServeHTTP(w, r)
2023-07-12 06:11:04 +00:00
} else {
http.ServeFile(w, r, indexHtmlPath)
}
2023-07-12 06:11:04 +00:00
}
2023-07-11 10:48:22 +00:00
}))
2023-07-11 05:07:44 +00:00
ServerAddress := ":9900"
log.Println("listen on ", ServerAddress)
log.Fatal(http.ListenAndServe(ServerAddress, mux))
}
2023-07-17 11:43:43 +00:00
// 后端服务的类型
2023-07-11 05:07:44 +00:00
type Backend struct {
HttpAddress string
Client *http.Client
Handler http.HandlerFunc
}
func NewBackend(mux *http.ServeMux, httpAddress string, muxPaths ...string) *Backend {
2023-07-17 11:43:43 +00:00
// 如果路径最后没有以'/'结尾,则添加'/'
2023-07-11 05:07:44 +00:00
for i, muxPath := range muxPaths {
if muxPath[len(muxPath)-1] != '/' {
muxPath = muxPath + "/"
muxPaths[i] = muxPath
}
}
2023-07-17 11:43:43 +00:00
// 创建HTTP客户端设置相关的超时参数和连接数限制
2023-07-11 05:07:44 +00:00
client := &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
2023-07-19 02:59:13 +00:00
Timeout: 60 * time.Second,
KeepAlive: 60 * time.Second,
2023-07-11 05:07:44 +00:00
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
2023-07-17 11:43:43 +00:00
// 创建后端服务对象,包含地址和客户端
2023-07-11 05:07:44 +00:00
backend := &Backend{
HttpAddress: httpAddress,
Client: client,
}
2023-07-17 11:43:43 +00:00
// 创建处理请求的函数
2023-07-11 05:07:44 +00:00
handleRequest := func(w http.ResponseWriter, r *http.Request) {
2023-07-17 11:43:43 +00:00
// 解析目标URL包含了查询参数
targetURL, err := url.Parse(httpAddress + r.URL.String())
2023-07-11 05:07:44 +00:00
if err != nil {
http.Error(w, "Error parsing target URL", http.StatusInternalServerError)
return
}
2023-07-17 11:43:43 +00:00
// 创建新的请求
2023-07-11 05:07:44 +00:00
proxyReq, err := http.NewRequest(r.Method, targetURL.String(), r.Body)
if err != nil {
http.Error(w, "Error creating proxy request", http.StatusInternalServerError)
return
}
// 复制原始请求的 Header
for key, values := range r.Header {
for _, value := range values {
proxyReq.Header.Add(key, value)
}
}
// 设置 Content-Length 和 Content-Type
proxyReq.ContentLength = r.ContentLength
proxyReq.Header.Set("Content-Type", r.Header.Get("Content-Type"))
2023-07-17 11:43:43 +00:00
// 发送请求
2023-07-11 05:07:44 +00:00
resp, err := backend.Client.Do(proxyReq)
if err != nil {
http.Error(w, "Error sending proxy request", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// 复制目标服务器的响应 Header
for key, values := range resp.Header {
for _, value := range values {
w.Header().Add(key, value)
}
}
// 转发目标服务器的响应状态码和主体
w.WriteHeader(resp.StatusCode)
_, err = io.Copy(w, resp.Body)
if err != nil {
http.Error(w, "Error copying proxy response", http.StatusInternalServerError)
return
}
}
2023-07-11 11:43:46 +00:00
2023-07-17 11:43:43 +00:00
// 为每个路径注册处理函数
2023-07-11 05:07:44 +00:00
for _, muxPath := range muxPaths {
mux.HandleFunc(muxPath, handleRequest)
}
2023-07-17 11:43:43 +00:00
// 返回后端服务对象
2023-07-11 05:07:44 +00:00
return backend
}
2023-07-19 02:59:13 +00:00
// get_zero_info.go
// Config 结构体用于解析yaml配置文件
type Config struct {
Host string `yaml:"Host"`
Port int `yaml:"Port"`
}
// Result 结构体用于存储解析结果
type Result struct {
FolderName string
Host string
Port int
PrefixRoute map[string]bool
}
// GetZeroInfo 遍历指定目录,并解析相关信息
func GetZeroInfo(rootDir string) (results []*Result) {
entries, err := ioutil.ReadDir(rootDir)
if err != nil {
log.Fatal(err)
}
for _, entry := range entries {
// 只处理目录类型
if entry.IsDir() {
result, err := findFoldersAndExtractInfo(rootDir, entry)
if err != nil {
log.Fatal(err)
}
results = append(results, result)
}
}
return
}
// findFoldersAndExtractInfo 查找目录并提取信息
func findFoldersAndExtractInfo(rootDir string, entry os.FileInfo) (*Result, error) {
var result *Result
folderName := entry.Name()
path := filepath.Join(rootDir, folderName)
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过非目录类型
if !info.IsDir() {
return nil
}
relPath, err := filepath.Rel(path, path)
if err != nil {
return err
}
// 跳过非当前目录的子目录
if strings.Contains(relPath, string(os.PathSeparator)) {
return filepath.SkipDir
}
// 读取配置文件
configPath := filepath.Join(path, "etc", folderName+".yaml")
routesPath := filepath.Join(path, "internal", "handler", "routes.go")
configContent, err := ioutil.ReadFile(configPath)
if err != nil {
return err
}
var config Config
err = yaml.Unmarshal(configContent, &config)
if err != nil {
return err
}
// 读取路由文件
routesContent, err := ioutil.ReadFile(routesPath)
if err != nil {
return err
}
PrefixRoute := extractPrefixRouteValues(string(routesContent))
// 构建结果
result = &Result{
FolderName: folderName,
Host: config.Host,
Port: config.Port,
PrefixRoute: PrefixRoute,
}
return filepath.SkipDir
})
if err != nil {
return nil, err
}
return result, nil
}
// extractPrefixRouteValues 提取路由前缀
func extractPrefixRouteValues(content string) map[string]bool {
lines := strings.Split(content, "\n")
var prefixPath map[string]bool = make(map[string]bool)
for _, line := range lines {
// 查找包含 "Path:" 的行
if strings.Contains(line, "Path:") {
path := strings.TrimSpace(strings.TrimPrefix(line, "Path:"))
paths := strings.Split(strings.Trim(path, `"`), "/")
path1 := "/" + paths[1] + "/" + paths[2]
if _, ok := prefixPath[path1]; !ok {
prefixPath[path1] = true
}
}
}
return prefixPath
}