基于Gin框架的网关设计开发

  年尾这几天自己研究并开发了一个基于Go语言的网关项目设计开发,并对其进行了开源,需要的小伙伴请点击:【Github地址】自主下载使用,README 中有还算详细的说明文档,本人不喜欢前端,所以也没有去找一个开源的后台页面接入,但提供了相应的数据添加接口,接口地址在项目【DOC】中说明,表结构设计也在【TABLE】中有非常详细的说明。

  另外本人还有另一个工具觉得很方便大家:【请求重新提交

使用注意

  1. 项目依赖都是必须的,其配置存放于【conf】配置中心
  2. 需要根据 TABLE 严格按照说明对MongoDB的数据结构和数据进行控制,否则不能正常使用
  3. 看完觉得不错,必须✨✨✨✨哦

项目依赖

  1. Gin 框架 
  2. Redis
  3. MongoDB

主要实现功能

  1. 路由转发,包括超时设置(基础功能 GET / POST)
  2. 多 IP / 域名 配置
  3. 路由 dns (只支持轮训)
  4. 请求限流
  5. 数据缓存
  6. 请求容错
  7. 统一鉴权

设计目标

  1. 模块化
  2. 配置化

 

入口:main 函数,为了方便后续的扩展,所以什么也不放

 

package main

import (
	"gw/route"
)

// main 函数没什么好说的,尽量干净,方便后续的模块添加
func main() {
	r := route.Route()
	r.Run(":1323")
}

 路由:路由采取了分隔,将前后端的路由分开

package route

import (
	"gw/route/admin"
	"gw/route/api"

	"github.com/gin-gonic/gin"
)

// 调用设置路由
func Route() *gin.Engine {
	r := gin.Default()
	//外部调用路由
	api.Route(r)

	//后台接口调用路由
	admin.Route(r)

	return r
}

 后台接口:提供了添加 group 和 wg 表的基本接口,让项目能跑起来

package admin

import (
	"gw/pkg/admin"

	"github.com/gin-gonic/gin"
)

func Route(r *gin.Engine) {
        // 路由访问转发的接口      
	r.POST("/req/add/api", admin.Add)

        // api 动态生成转发接口的表添加
	r.POST("/req/add/group", admin.AddGroup)
}

 前台接口:动态循环路由组(group),将其配置添加到模糊路由中,为了解决后续增加 wg 表的配置需要重启

package api

import (
	"fmt"

	"gw/pkg/api"
	"gw/library"
	"gw/pkg/middle"

	"github.com/gin-gonic/gin"
)

func Route(r *gin.Engine) {
	list, err := library.Group("group")
	if err != nil {
		panic(fmt.Sprintf("Api Route Was Wrong Err Was %s", err))
	}

	//动态加载路由,根据mongoDB中的path加载
	for _, v := range list {
		pth := v.Group
		r.Any(fmt.Sprintf("%s%s", pth, "*action"), api.Run, middle.Body())
	}
}

 执行程序入口:该入口是整个系统的逻辑入口,目前还没想到更好的方式

package api

import (
	"fmt"
	"net/http"
	"time"

	"gw/conf"
	"gw/library"
	"gw/pkg/ds"
	"gw/pkg/dy"
	"gw/pkg/fw"
	"gw/util"

	"github.com/gin-gonic/gin"
)

// 入口函数
func Run(c *gin.Context) {
	t := time.NewTimer(conf.RequestTimeOut * time.Second)
	//设置 global
	var glb G
	glb.Rch = make(chan string)
	glb.Ech = make(chan error)

	go func(c *gin.Context, glb *G) {
		glb.RequestTime = util.GetTime()

		//设置请求访问的数据
		if err := glb.SetInfo(c); err != nil {
			glb.Ech <- err
			return
		}

		//容错
		decay := dy.Decay{
			Open:      glb.Md.Decay,
			DecayTime: glb.Md.DecayTime,
			Ctx:       c,
		}
		decayBody := decay.Start()
		if decayBody != "" {
			glb.Rch <- decayBody
			return
		}

		//获取要访问的url
		dns := ds.Dns{
			Ds:  glb.Md.Dns,
			Pth: glb.Md.To,
			Ctx: c,
		}
		dns.GetRestUrl()
		glb.To = dns.To
		glb.Query = dns.Query

		//流量检查
		flow := fw.Flow{
			Path: glb.To,
			Num:  glb.Md.Flow,
		}
		if err := flow.Check(); err != nil {
			glb.Ech <- err
			return
		}

		//发起请求
		hp := library.HttpRequest{
			Method:    glb.Md.Method,
			To:        glb.To,
			Query:     glb.Query,
			Out:       glb.Md.Timeout,
			CacheTime: glb.Md.CacheTime,
		}

		//发起请求
		body, err := hp.Http()
		if err != nil {
			glb.Ech <- err
			return
		}

		//写入上下文,目前用于容错
		c.Set("RequestBody", body)

		glb.Rch <- body
	}(c, &glb)

	select {
	case rch := <-glb.Rch:
		c.String(http.StatusOK, rch)
	case ech := <-glb.Ech:
		c.String(http.StatusInternalServerError, fmt.Sprintln(ech))
	case <-t.C:
		c.String(http.StatusNotFound, "request time out")
	}

	t.Stop()
}

 

从上述代码中可以看到大致的项目设计了,具体功能代码不再单独贴图了,大家可以拿到代码后看一下,项目设计的挺简单的,很方便扩展

posted @ 2019-12-31 17:14  阿波罗一号  阅读(2068)  评论(0编辑  收藏  举报