基于Go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)易api網(wǎng)關(guān)的示例代碼
瀏覽器的請(qǐng)求去請(qǐng)求目標(biāo)地址,然后獲得結(jié)果它再發(fā)送給瀏覽器。對(duì)于Go語(yǔ)言來(lái)說(shuō),實(shí)現(xiàn)轉(zhuǎn)發(fā)只需要簡(jiǎn)單的一行代碼即可實(shí)現(xiàn),如下所示:
httputil.NewSingleHostReverseProxy(address)
基于此功能,進(jìn)行簡(jiǎn)單包裝,實(shí)現(xiàn)從遠(yuǎn)端admin管理中心獲取需要轉(zhuǎn)發(fā)的路由信息或者可以從本地配置文件中獲取,實(shí)現(xiàn)動(dòng)態(tài)轉(zhuǎn)發(fā)。后續(xù)可以根據(jù)業(yè)務(wù)情況,可以實(shí)現(xiàn)如下功能:
開(kāi)發(fā)接口,實(shí)現(xiàn)動(dòng)態(tài)添加代理規(guī)則,進(jìn)行轉(zhuǎn)發(fā)
- 過(guò)濾不合法的接口
- 接口限流
- 統(tǒng)一日志記錄
- …
代碼如下:
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 //轉(zhuǎn)發(fā)的前綴判斷
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", "", "環(huán)境")
var proxyFile = flag.String("proxyFile", "", "測(cè)試環(huán)境的數(shù)據(jù)")
//日志初始化
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("打開(kāi)日志文件失?。?, 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() //創(chuàng)建一個(gè)router
flag.Parse()
initLog()
if *profile != "" {
InfoLog.Printf("加載遠(yuǎn)端數(shù)據(jù): %s ", *adminUrl)
initProxyList()
} else {
InfoLog.Printf("加載本地配置數(shù)據(jù): %s", *proxyFile)
loadProxyListFromFile()
}
router.Any("/*action", Forward) //所有請(qǐng)求都會(huì)經(jīng)過(guò)Forward函數(shù)轉(zhuǎn)發(fā)
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 {
//追加 反斜杠,為了動(dòng)態(tài)匹配的時(shí)候 防止 /proxy/test /proxy/test1 無(wú)法正確轉(zhuǎn)發(fā)
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
}
//從內(nèi)存里面獲取轉(zhuǎn)發(fā)的url
var upstream = ""
if value, ok := proxyMap[r.RequestURI]; ok {
//如果轉(zhuǎn)發(fā)的地址是 / 開(kāi)頭的,需要去掉
if strings.HasSuffix(value.Upstream, "/") {
upstream += strings.TrimRight(value.Upstream, "/")
} else {
upstream += value.Upstream
}
//如果首位不是/開(kāi)頭,則需要追加
if !strings.HasPrefix(value.RewritePrefix, "/") {
upstream += "/" + value.RewritePrefix
} else {
upstream += value.RewritePrefix
}
//去掉開(kāi)頭
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
// 創(chuàng)建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 格式如下:
{
"success":true,
"status": "ok",
"data": [
{
"remark": "測(cè)試環(huán)境",
"prefix": "/division",
"upstream": "http://test.xxxxx.cn/",
"rewritePrefix": "/api/division"
},
{
"remark": "測(cè)試環(huán)境1",
"prefix": "/division1",
"upstream": "http://test.xxxx.cn/",
"rewritePrefix": ""
},
{
"remark": "測(cè)試環(huán)境2",
"prefix": "/division3",
"upstream": "http://test.xxxxxx.cn/",
"rewritePrefix": "/api/division"
}
]
}
啟動(dòng)腳本
## 加載本地配置文件數(shù)據(jù) go run proxy_agent.go -proxyFile ./proxy_data.json ## 啟動(dòng)從配置中心獲取數(shù)據(jù) go run proxy_agent.go -profile prod -adminUrl http://localhost:3000/proxy/findAll
到此這篇關(guān)于基于Go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)易api網(wǎng)關(guān)的示例代碼的文章就介紹到這了,更多相關(guān)Go api網(wǎng)關(guān) 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言goto語(yǔ)句跳轉(zhuǎn)到指定的標(biāo)簽實(shí)現(xiàn)方法
這篇文章主要介紹了go語(yǔ)言goto語(yǔ)句跳轉(zhuǎn)到指定的標(biāo)簽實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換
本文主要介紹了golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
golang通過(guò)遞歸遍歷生成樹(shù)狀結(jié)構(gòu)的操作
這篇文章主要介紹了golang通過(guò)遞歸遍歷生成樹(shù)狀結(jié)構(gòu)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
GO語(yǔ)言協(xié)程互斥鎖Mutex和讀寫鎖RWMutex用法實(shí)例詳解
這篇文章主要介紹了GO語(yǔ)言協(xié)程互斥鎖Mutex和讀寫鎖RWMutex用法詳解,需要的朋友可以參考下2022-04-04
golang?pprof?監(jiān)控系列?go?trace統(tǒng)計(jì)原理與使用解析
這篇文章主要為大家介紹了golang?pprof?監(jiān)控系列?go?trace統(tǒng)計(jì)原理與使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04

