91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用Go語言實現的api網關

發布時間:2021-12-08 11:08:20 來源:億速云 閱讀:285 作者:小新 欄目:開發技術

小編給大家分享一下如何使用Go語言實現的api網關,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

瀏覽器的請求去請求目標地址,然后獲得結果它再發送給瀏覽器。對于Go語言來說,實現轉發只需要簡單的一行代碼即可實現,如下所示:

httputil.NewSingleHostReverseProxy(address)

基于此功能,進行簡單包裝,實現從遠端admin管理中心獲取需要轉發的路由信息或者可以從本地配置文件中獲取,實現動態轉發。后續可以根據業務情況,可以實現如下功能:
開發接口,實現動態添加代理規則,進行轉發

  • 過濾不合法的接口

  • 接口限流

  • 統一日志記錄

代碼如下:

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 格式如下:

{
  "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"
    }
  ]
}

啟動腳本

## 加載本地配置文件數據
go run proxy_agent.go -proxyFile ./proxy_data.json
## 啟動從配置中心獲取數據
go run proxy_agent.go -profile prod -adminUrl http://localhost:3000/proxy/findAll

看完了這篇文章,相信你對“如何使用Go語言實現的api網關”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

敖汉旗| 天等县| 隆安县| 屯留县| 玉溪市| 顺义区| 祁连县| 冕宁县| 阳山县| 安远县| 富阳市| 油尖旺区| 布尔津县| 常宁市| 上饶县| 丰县| 祥云县| 桑植县| 永春县| 永靖县| 诸城市| 栾城县| 商河县| 灵武市| 盘山县| 会昌县| 海口市| 青川县| 西安市| 定西市| 南投县| 油尖旺区| 金寨县| 青浦区| 永吉县| 开化县| 新巴尔虎右旗| 涿鹿县| 油尖旺区| 定日县| 西充县|