瀏覽器的請求去請求目標地址,然后獲得結果它再發送給瀏覽器。對于Go語言來說,實現轉發只需要簡單的一行代碼即可實現,如下所示:
1
|
httputil.NewSingleHostReverseProxy(address) |
基于此功能,進行簡單包裝,實現從遠端admin管理中心獲取需要轉發的路由信息或者可以從本地配置文件中獲取,實現動態轉發。后續可以根據業務情況,可以實現如下功能:
開發接口,實現動態添加代理規則,進行轉發
- 過濾不合法的接口
- 接口限流
- 統一日志記錄
- …
代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
package main import ( "encoding/json" "flag" "fmt" "github.com/gin-gonic/gin" "io" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "os" "strings" ) type Respond struct { Success bool Status string Data []Proxy } type Proxy struct { Remark string //描述 Prefix string //轉發的前綴判斷 Upstream string //后端 nginx 地址或者ip地址 RewritePrefix string //重寫 } var ( InfoLog *log.Logger ErrorLog *log.Logger proxyMap = make(map[string]Proxy) ) var adminUrl = flag.String("adminUrl", "", "admin的地址") var profile = flag.String("profile", "", "環境") var proxyFile = flag.String("proxyFile", "", "測試環境的數據") //日志初始化 func initLog() { errFile, err := os.OpenFile("errors.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) infoFile, err := os.OpenFile("info.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { log.Fatalln("打開日志文件失敗:", err) } InfoLog = log.New(io.MultiWriter(os.Stderr, infoFile), "Info:", log.LstdFlags|log.Lmicroseconds|log.Lshortfile) ErrorLog = log.New(io.MultiWriter(os.Stderr, errFile), "Error:", log.LstdFlags|log.Lmicroseconds|log.Lshortfile) } func main() { router := gin.Default() //創建一個router flag.Parse() initLog() if *profile != "" { InfoLog.Printf("加載遠端數據: %s ", *adminUrl) initProxyList() } else { InfoLog.Printf("加載本地配置數據: %s", *proxyFile) loadProxyListFromFile() } router.Any("/*action", Forward) //所有請求都會經過Forward函數轉發 router.Run(":8000") } func initProxyList() { resp, _ := http.Get(*adminUrl) if resp != nil && resp.StatusCode == 200 { bytes, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { fmt.Println("ioutil.ReadAll err=", err) return } var respond Respond err = json.Unmarshal(bytes, &respond) if err != nil { fmt.Println("json.Unmarshal err=", err) return } proxyList := respond.Data for _, proxy := range proxyList { //追加 反斜杠,為了動態匹配的時候 防止 /proxy/test /proxy/test1 無法正確轉發 proxyMap[proxy.Prefix+"/"] = proxy } } } func Forward(c *gin.Context) { HostReverseProxy(c.Writer, c.Request) } func HostReverseProxy(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/favicon.ico" { io.WriteString(w, "Request path Error") return } //從內存里面獲取轉發的url var upstream = "" if value, ok := proxyMap[r.RequestURI]; ok { //如果轉發的地址是 / 開頭的,需要去掉 if strings.HasSuffix(value.Upstream, "/") { upstream += strings.TrimRight(value.Upstream, "/") } else { upstream += value.Upstream } //如果首位不是/開頭,則需要追加 if !strings.HasPrefix(value.RewritePrefix, "/") { upstream += "/" + value.RewritePrefix } else { upstream += value.RewritePrefix } //去掉開頭 r.URL.Path = strings.ReplaceAll(r.URL.Path, r.RequestURI, "") } // parse the url remote, err := url.Parse(upstream) InfoLog.Printf("RequestURI %s upstream %s remote %s", r.RequestURI, upstream, remote) if err != nil { panic(err) } r.URL.Host = remote.Host r.URL.Scheme = remote.Scheme r.Header.Set("X-Forwarded-Host", r.Header.Get("Host")) r.Host = remote.Host httputil.NewSingleHostReverseProxy(remote).ServeHTTP(w, r) } func loadProxyListFromFile() { file, err := os.Open(*proxyFile) if err != nil { ErrorLog.Println("err:", err) } var respond Respond // 創建json解碼器 decoder := json.NewDecoder(file) err = decoder.Decode(&respond) if err != nil { fmt.Println("LoadProxyListFromFile failed", err.Error()) } proxyList := respond.Data for _, proxy := range proxyList { proxyMap[proxy.Prefix+"/"] = proxy } } |
proxy_data.json 格式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
{ "success" : true , "status" : "ok" , "data" : [ { "remark" : "測試環境" , "prefix" : "/division" , "upstream" : "http://test.xxxxx.cn/" , "rewritePrefix" : "/api/division" }, { "remark" : "測試環境1" , "prefix" : "/division1" , "upstream" : "http://test.xxxx.cn/" , "rewritePrefix" : "" }, { "remark" : "測試環境2" , "prefix" : "/division3" , "upstream" : "http://test.xxxxxx.cn/" , "rewritePrefix" : "/api/division" } ] } |
啟動腳本
1
2
3
4
|
## 加載本地配置文件數據 go run proxy_agent.go -proxyFile ./proxy_data.json ## 啟動從配置中心獲取數據 go run proxy_agent.go -profile prod -adminUrl http://localhost:3000/proxy/findAll |
到此這篇關于基于Go語言實現的簡易api網關的示例代碼的文章就介紹到這了,更多相關Go api網關 內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/dong945221578/article/details/110679480