gateway

package module
v1.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2022 License: MIT Imports: 10 Imported by: 0

README

gateway

实例化
gw = NewGateway(0, func() (options []Option) {
    return []Option{
        Option{
            Name:        "登录",
            Path:        "user/login",
            SecondLimit: 100,
            BucketSize: 8,
        },
        Option{
            Name:        "获取轮播图",
            Path:        "config/scrolls",
            SecondLimit: 0,
        },
    }
})
Option解析
type Option struct {
	Name        string `json:"name" yaml:"name"`                       //方法名称
	Path        string `json:"path" yaml:"path"`                       //方法路径
	SecondLimit int    `json:"second_limit" yaml:"second_limit"`       //每秒限速,-1降级,0不限速,默认不限速 
	BucketSize  int    `json:"bucket_size" yaml:"bucket_size"`         //令牌桶限速,桶容量
}
在gin中使用

app.yml

- name: '登录'
  path: '/user/login'
  second_limit: 0

- name: '注册'
  path: '/user/regis'
  second_limit: -1

- name: '获取用户信息'
  path: '/user/info'
  second_limit: 2000
  bucket_size: 20

main.go

package main

import (
  "math/rand"
  "net/http"
  "os"
  "time"

  "github.com/gin-gonic/gin"
  "github.com/grpc-boot/base"
  "github.com/grpc-boot/gateway"
  jsoniter "github.com/json-iterator/go"
)

var (
  gw gateway.Gateway
)

const (
  LogicCode = `logic:code`
)

type Response struct {
  Code int         `json:"code"`
  Msg  string      `json:"msg"`
  Data interface{} `json:"data"`
}

func init() {
  optionFunc := gateway.OptionsWithJsonFile("app.json")
  //optionFunc := gateway.OptionsWithYamlFile("app.yml")
  gw = gateway.NewGateway(0, optionFunc)
}

func response(ctx *gin.Context, code int, msg string, data interface{}) {
  ctx.Set(LogicCode, code)

  result, _ := jsoniter.Marshal(Response{
    Code: code,
    Msg:  msg,
    Data: data,
  })

  ctx.Data(http.StatusOK, "application/json", result)
}

func withGateway() gin.HandlerFunc {
  return func(ctx *gin.Context) {
    path, accessTime := ctx.FullPath(), time.Now()
    status, exists := gw.In(path)

    switch status {
    case gateway.StatusNo:
      var (
        code = http.StatusRequestTimeout
        msg  = "server is busy"
      )

      response(ctx, code, msg, nil)
      gw.Out(accessTime, path, code)
      ctx.Abort()
      return
    case gateway.StatusBusy: //超时
      response(ctx, http.StatusRequestTimeout, "server is busy", nil)
      gw.Out(accessTime, path, http.StatusRequestTimeout)
      ctx.Abort()
      return
    }

    //默认设置为200
    ctx.Set(LogicCode, http.StatusOK)

    //handler
    ctx.Next()

    if exists {
      //网关出
      duration, qps, total, er := gw.Out(accessTime, path, ctx.GetInt(LogicCode))
      base.Green("path:%s duration:%v qps:%d total:%d err:%v", path, duration, qps, total, er)
    }
  }
}

func main() {
  defer gw.Close()

  rand.Seed(time.Now().UnixNano())
  router := gin.New()

  router.Use(withGateway())

  router.GET("/gw", func(ctx *gin.Context) {
    response(ctx, http.StatusOK, "ok", gw.Info())
  })

  router.GET("/user/regis", func(ctx *gin.Context) {
    time.Sleep(time.Millisecond * time.Duration(rand.Int63n(1000)))
    if time.Now().Unix()%2 == 0 {
      response(ctx, http.StatusOK, "ok", nil)
      return
    }

    response(ctx, http.StatusCreated, "ok", nil)
  })

  router.GET("/user/login", func(ctx *gin.Context) {
    time.Sleep(time.Millisecond * time.Duration(rand.Int63n(10)))
    if time.Now().Unix()%2 == 0 {
      response(ctx, http.StatusOK, "ok", nil)
      return
    }

    response(ctx, http.StatusForbidden, "forbidden", nil)
  })

  router.GET("/user/info", func(ctx *gin.Context) {
    time.Sleep(time.Millisecond * time.Duration(rand.Int63n(1000)))

    if time.Now().Unix()%2 == 0 {
      response(ctx, http.StatusOK, "ok", nil)
      return
    }

    response(ctx, http.StatusNotFound, "not found", nil)
  })

  server := &http.Server{
    Addr:    ":8080",
    Handler: router,
  }

  err := server.ListenAndServe()
  if err != nil {
    os.Exit(1)
  }
}
用redis做options配置存储
package main

import (
  "time"

  "github.com/grpc-boot/base"
  "github.com/grpc-boot/gateway"

  redigo "github.com/garyburd/redigo/redis"
  jsoniter "github.com/json-iterator/go"
)

const (
  hashKey   = "gateway:options"
  redisAddr = `127.0.0.1:6379`
)

var (
  redisPool *redigo.Pool
  gw        gateway.Gateway
)

func init() {
  dialOptions := []redigo.DialOption{
    redigo.DialReadTimeout(time.Millisecond * 500),
  }

  redisPool = &redigo.Pool{
    MaxIdle:   1,
    MaxActive: 1,
    Dial: func() (redigo.Conn, error) {
      return redigo.Dial("tcp", redisAddr, dialOptions...)
    },
    TestOnBorrow: func(c redigo.Conn, t time.Time) error {
      if time.Since(t) < time.Minute {
        return nil
      }
      _, err := c.Do("PING")
      return err
    },
  }

  conn := redisPool.Get()
  defer conn.Close()

  args := []interface{}{
    hashKey,
    1,
    `{"name": "登录","path": "/user/login","second_limit": 0}`,
    2,
    `{"name": "注册","path": "/user/regis","second_limit": -1}`,
    3,
    `{"name": "获取用户信息","path": "/user/info","second_limit": 1000, "bucket_size": 10}`,
  }

  _, err := conn.Do("HMSET", args...)
  if err != nil {
    base.RedFatal("set redis config err:%s", err.Error())
  }
}

func main() {
  gw = gateway.NewGateway(time.Second, gateway.OptionsWithRedis(redisPool, hashKey))

  go func() {
    for {
      info, _ := jsoniter.Marshal(gw.Info())
      base.Green("%s", string(info))
      time.Sleep(time.Second)
    }
  }()

  var wa chan struct{}
  <-wa

  gw.Close()
}

用mysql做options配置存储
package main

import (
  "database/sql"
  "sync"
  "time"

  "github.com/grpc-boot/base"
  "github.com/grpc-boot/gateway"

  _ "github.com/go-sql-driver/mysql"
)

/**
CREATE TABLE `gateway` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '名称',
  `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '路径',
  `second_limit` int DEFAULT '5000' COMMENT '每秒请求数',
  `bucket_size` int unsigned DEFAULT '8' COMMENT '桶容量',
  PRIMARY KEY (`id`),
  UNIQUE KEY `path` (`path`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/

const (
  tableName = "gateway"
  dsn       = `root:12345678@tcp(127.0.0.1:3306)/dd?timeout=5s&readTimeout=6s`
)

var (
  cache sync.Map
  db    *sql.DB
  gw    gateway.Gateway
)

func init() {
  var err error
  db, err = sql.Open("mysql", dsn)
  if err != nil {
    base.RedFatal("open mysql err:%s", err.Error())
  }

  db.SetConnMaxLifetime(time.Second * 100)
  db.SetConnMaxIdleTime(time.Second * 100)
  db.SetMaxOpenConns(5)
  db.SetMaxIdleConns(5)

  args := []interface{}{
    "登录", "/user/login", "0", "10",
    "注册", "/user/regis", "-1", "3",
    "获取用户信息", "/user/info", "10", "8",
  }

  _, err = db.Exec("INSERT IGNORE INTO `gateway`(`name`,`path`,`second_limit`,`bucket_size`)VALUES(?,?,?,?),(?,?,?,?),(?,?,?,?)", args...)
  if err != nil {
    base.RedFatal("insert config err:%s", err.Error())
  }
}

func main() {
  gw = gateway.NewGateway(time.Second, gateway.OptionsWithDb(db, tableName))

  cache.Store("gw", gw)

  /*go func() {
  	for {
  		info, _ := jsoniter.Marshal(gw.Info())
  		base.Green("%s", string(info))
  		time.Sleep(time.Second)
  	}
  }()*/

  for i := 1; i < 8; i++ {
    go access()
  }

  var wa chan struct{}
  <-wa

  gw.Close()
}

func access() {
  for {
    go func() {
      val, _ := cache.Load("gw")
      gwy, _ := val.(gateway.Gateway)
      status, _ := gwy.In("/user/info")
      base.Fuchsia("%d", status)
    }()
    time.Sleep(time.Millisecond * 10)
  }
}

Documentation

Index

Constants

View Source
const (
	StatusNo   = 0
	StatusYes  = 1
	StatusBusy = 2
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Gateway

type Gateway interface {
	// In 接收请求
	In(path string) (status uint8, exists bool)
	// Out gateway响应
	Out(accessTime time.Time, path string, code int) (dur time.Duration, qps int32, total uint64, err error)
	// Info 获取gateway信息
	Info() (info Info)
	// Close 释放gateway资源
	Close() (err error)
}

func NewGateway

func NewGateway(duration time.Duration, optionsFunc OptionsFunc) Gateway

NewGateway new gateway intance

type Info

type Info struct {
	Qps        int32        `json:"qps"`
	Total      uint64       `json:"total"`
	MethodList []MethodInfo `json:"method_list"`
}

Info gateway详情

type LatencyList

type LatencyList []time.Duration

LatencyList 方法延时列表

func (LatencyList) Avg

func (ll LatencyList) Avg() time.Duration

func (LatencyList) L90

func (ll LatencyList) L90() time.Duration

func (LatencyList) L95

func (ll LatencyList) L95() time.Duration

func (LatencyList) Len

func (ll LatencyList) Len() int

func (LatencyList) Less

func (ll LatencyList) Less(i, j int) bool

func (LatencyList) Max

func (ll LatencyList) Max() time.Duration

func (LatencyList) Min

func (ll LatencyList) Min() time.Duration

func (LatencyList) Swap

func (ll LatencyList) Swap(i, j int)

type MethodInfo

type MethodInfo struct {
	Name        string         `json:"name"`
	Path        string         `json:"path"`
	SecondLimit int            `json:"second_limit"`
	Qps         int32          `json:"qps"`
	Total       uint64         `json:"total"`
	Avg         string         `json:"avg"`
	Min         string         `json:"min"`
	Max         string         `json:"max"`
	L90         string         `json:"90_line"`
	L95         string         `json:"95_line"`
	CodeMap     map[int]uint64 `json:"code_map"`
	// contains filtered or unexported fields
}

MethodInfo 方法详情

type Option

type Option struct {
	Name        string `json:"name" yaml:"name"`
	Path        string `json:"path" yaml:"path"`
	SecondLimit int    `json:"second_limit" yaml:"second_limit"`
	BucketSize  int    `json:"bucket_size" yaml:"bucket_size"`
}

Option 方法配置

type OptionsFunc

type OptionsFunc func() (options []Option)

OptionsFunc 加载配置函数

func OptionsWithDb

func OptionsWithDb(db *sql.DB, tableName string) (optionsFunc OptionsFunc)

OptionsWithDb 从数据库表加载配置

func OptionsWithJsonFile

func OptionsWithJsonFile(filepath string) (optionsFunc OptionsFunc)

OptionsWithJsonFile 从Json文件加载配置

func OptionsWithRedis

func OptionsWithRedis(red *redigo.Pool, hashKey string) (optionsFunc OptionsFunc)

OptionsWithRedis 从redis哈希Key加载配置

func OptionsWithYamlFile

func OptionsWithYamlFile(filepath string) (optionsFunc OptionsFunc)

OptionsWithYamlFile 从Yaml文件加载配置

Directories

Path Synopsis
gin

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL