首页后端开发其他后端知识Go语言下实现的简易api网关的方法和代码是什么?

Go语言下实现的简易api网关的方法和代码是什么?

时间2024-03-26 20:16:03发布访客分类其他后端知识浏览854
导读:在这篇文章中,我们将学习“Go语言下实现的简易api网关的方法和代码是什么?”的相关知识,下文有详细的介绍及示例,小编觉得挺不错的,有需要的朋友可以借鉴参考,希望对大家阅读完这篇能有所获。 浏览器的请求去请求目标地址,然后获得...
在这篇文章中,我们将学习“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网关的方法和代码是什么?”已经有了进一步的了解,更多相关的问题,欢迎关注网络或到官网咨询客服。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Go语言下实现的简易api网关的方法和代码是什么?
本文地址: https://pptw.com/jishu/653733.html
Go语言中switch条件语句用法是什么,有什么应用 Bootstrap信息提示框的用法是什么,触发方式是怎样

游客 回复需填写必要信息