年尾这几天自己研究并开发了一个基于Go语言的网关项目设计开发,并对其进行了开源,需要的小伙伴请点击:【Github地址】自主下载使用,README 中有还算详细的说明文档,本人不喜欢前端,所以也没有去找一个开源的后台页面接入,但提供了相应的数据添加接口,接口地址在项目【DOC】中说明,表结构设计也在【TABLE】中有非常详细的说明。
另外本人还有另一个工具觉得很方便大家:【请求重新提交】
使用注意:
- 项目依赖都是必须的,其配置存放于【conf】配置中心
- 需要根据 TABLE 严格按照说明对MongoDB的数据结构和数据进行控制,否则不能正常使用
- 看完觉得不错,必须✨✨✨✨哦
项目依赖:
- Gin 框架
- Redis
- MongoDB
主要实现功能:
- 路由转发,包括超时设置(基础功能 GET / POST)
- 多 IP / 域名 配置
- 路由 dns (只支持轮训)
- 请求限流
- 数据缓存
- 请求容错
- 统一鉴权
设计目标:
- 模块化
- 配置化
入口: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()
}
从上述代码中可以看到大致的项目设计了,具体功能代码不再单独贴图了,大家可以拿到代码后看一下,项目设计的挺简单的,很方便扩展
