文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基于Go语言实现的简易api网关的示例代码

2022-06-07 20:42

关注

浏览器的请求去请求目标地址,然后获得结果它再发送给浏览器。对于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网关的示例代码的文章就介绍到这了,更多相关Go api网关 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯