go语言web开发系列之十四:用gin框架实现基于ip地址的限流
一,安装用到的库
1,从命令行安装 x/time/rate库
iuhongdi@ku:~$ go get -u golang.org/x/time/rate
说明:刘宏缔的go森林是一个专注golang的博客,
地址:https://blog.csdn.net/weixin_43881017
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,地址:
https://github.com/liuhongdi/digv14
2,功能说明:
演示了针对ip的地址的限流,
限制同一ip地址在单位时间内可以发起请求的次数
3,项目结构:如图:
三,配置文件
config/config.yaml
-
Database:
-
DBType: mysql
-
UserName: root
-
Password: password
-
Host: 127.0.0.1:3306
-
DBName: dig
-
Charset: utf8
-
ParseTime: True
-
MaxIdleConns: 10
-
MaxOpenConns: 30
-
Server:
-
RunMode: debug
-
HttpPort: 8000
-
ReadTimeout: 60
-
WriteTimeout: 60
-
Log:
-
LogFilePath: /data/gologs/logs
-
LogInfoFileName: info
-
LogWarnFileName: warn
-
LogFileExt: log
-
AccessLog:
-
LogFilePath: /data/gologs/logs
-
LogFileName: access
-
LogFileExt: log
-
Limiter:
-
CountPerSecond: 7
四,go代码说明
1,global/limiter.go
-
package global
-
-
import (
-
"golang.org/x/time/rate"
-
"sync"
-
)
-
-
type IPRateLimiter struct {
-
ips map[string]*rate.Limiter
-
mu *sync.RWMutex
-
r rate.Limit
-
b int
-
}
-
-
var (
-
RateLimiter *IPRateLimiter
-
)
-
-
// 创建一个RateLimiter
-
func SetupIPRateLimiter() (error) {
-
var r rate.Limit
-
r=1
-
b := LimiterSetting.CountPerSecond
-
RateLimiter = &IPRateLimiter{
-
ips: make(map[string]*rate.Limiter),
-
mu: &sync.RWMutex{},
-
r: r,
-
b: b,
-
}
-
return nil
-
}
-
-
// 添加一个ip到map
-
func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter {
-
i.mu.Lock()
-
defer i.mu.Unlock()
-
-
limiter := rate.NewLimiter(i.r, i.b)
-
i.ips[ip] = limiter
-
return limiter
-
}
-
-
//通过ip得到limiter
-
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
-
i.mu.Lock()
-
limiter, exists := i.ips[ip]
-
if !exists {
-
i.mu.Unlock()
-
return i.AddIP(ip)
-
}
-
i.mu.Unlock()
-
return limiter
-
}
2,global/setting.go
-
package global
-
-
import (
-
"fmt"
-
"github.com/liuhongdi/digv14/pkg/setting"
-
"time"
-
)
-
//服务器配置
-
type ServerSettingS struct {
-
RunMode string
-
HttpPort string
-
ReadTimeout time.Duration
-
WriteTimeout time.Duration
-
}
-
//数据库配置
-
type DatabaseSettingS struct {
-
DBType string
-
UserName string
-
Password string
-
Host string
-
DBName string
-
Charset string
-
ParseTime bool
-
MaxIdleConns int
-
MaxOpenConns int
-
}
-
//日志配置
-
type LogSettingS struct {
-
LogFilePath string //保存到的目录
-
LogInfoFileName string //info级日志文件的名字
-
LogWarnFileName string //warn级日志文件的名字
-
LogAccessFileName string //Access日志文件的名字
-
LogFileExt string //文件的扩展名
-
}
-
//访问日志配置
-
type AccessLogSettingS struct {
-
LogFilePath string //保存到的目录
-
LogFileName string //Access日志文件的名字
-
LogFileExt string //文件的扩展名
-
}
-
//限流配置
-
type LimiterSettingS struct {
-
CountPerSecond int //每秒的访问次数
-
}
-
//定义全局变量
-
var (
-
ServerSetting *ServerSettingS
-
DatabaseSetting *DatabaseSettingS
-
LogSetting *LogSettingS
-
AccessLogSetting *AccessLogSettingS
-
LimiterSetting *LimiterSettingS
-
)
-
-
//读取配置到全局变量
-
func SetupSetting() error {
-
s, err := setting.NewSetting()
-
if err != nil {
-
return err
-
}
-
err = s.ReadSection("Database", &DatabaseSetting)
-
if err != nil {
-
return err
-
}
-
-
err = s.ReadSection("Server", &ServerSetting)
-
if err != nil {
-
return err
-
}
-
-
err = s.ReadSection("Log", &LogSetting)
-
if err != nil {
-
return err
-
}
-
-
err = s.ReadSection("AccessLog", &AccessLogSetting)
-
if err != nil {
-
return err
-
}
-
-
err = s.ReadSection("Limiter", &LimiterSetting)
-
if err != nil {
-
return err
-
}
-
-
fmt.Println("setting:")
-
fmt.Println(ServerSetting)
-
fmt.Println(DatabaseSetting)
-
fmt.Println(LogSetting)
-
fmt.Println(AccessLogSetting)
-
fmt.Println(LimiterSetting)
-
return nil
-
}
3,main.go
-
package main
-
-
import (
-
"github.com/gin-gonic/gin"
-
_ "github.com/jinzhu/gorm/dialects/mysql"
-
"github.com/liuhongdi/digv14/global"
-
"github.com/liuhongdi/digv14/router"
-
"log"
-
)
-
-
//init
-
func init() {
-
//setting
-
err := global.SetupSetting()
-
if err != nil {
-
log.Fatalf("init.setupSetting err: %v", err)
-
}
-
-
//logger
-
err = global.SetupLogger()
-
if err != nil {
-
log.Fatalf("init.SetupLogger err: %v", err)
-
}
-
-
//access logger
-
err = global.SetupAccessLogger()
-
if err != nil {
-
log.Fatalf("init.SetupAccessLogger err: %v", err)
-
}
-
-
//db
-
err = global.SetupDBLink()
-
if err != nil {
-
log.Fatalf("init.SetupLogger err: %v", err)
-
global.Logger.Fatalf("init.setupDBEngine err: %v", err)
-
}
-
-
//ratelimiter
-
err = global.SetupIPRateLimiter()
-
if err != nil {
-
log.Fatalf("init.SetupIPRateLimiter err: %v", err)
-
global.Logger.Fatalf("init.SetupIPRateLimiter err: %v", err)
-
}
-
-
global.Logger.Infof("------应用init结束")
-
//global.Logger.
-
}
-
-
func main() {
-
global.Logger.Infof("------应用main函数开始")
-
//设置运行模式
-
gin.SetMode(global.ServerSetting.RunMode)
-
//引入路由
-
r := router.Router()
-
//run
-
r.Run(":"+global.ServerSetting.HttpPort)
-
}
4,middleware/limit.go
-
package middleware
-
-
import (
-
"fmt"
-
"github.com/gin-gonic/gin"
-
"github.com/liuhongdi/digv14/global"
-
"github.com/liuhongdi/digv14/pkg/result"
-
"github.com/liuhongdi/digv14/pkg/util"
-
)
-
//限流器
-
func LimitMiddleware() gin.HandlerFunc {
-
return func(c *gin.Context) {
-
//得到ip地址
-
ipAddr:=util.GetRealIp(c)
-
fmt.Println("current ip:"+ipAddr)
-
//ipAddr:="127.0.0.1"
-
limiter := global.RateLimiter.GetLimiter(ipAddr)
-
if !limiter.Allow() {
-
fmt.Println("not allow,will return")
-
resultRes := result.NewResult(c)
-
resultRes.Error(2004,"访问超出限制")
-
return
-
} else {
-
fmt.Println("allow,next")
-
c.Next()
-
}
-
}
-
}
5,router/router.go
-
package router
-
-
import (
-
"github.com/gin-gonic/gin"
-
"github.com/liuhongdi/digv14/controller"
-
"github.com/liuhongdi/digv14/global"
-
"github.com/liuhongdi/digv14/middleware"
-
"github.com/liuhongdi/digv14/pkg/result"
-
"runtime/debug"
-
)
-
-
func Router() *gin.Engine {
-
router := gin.Default()
-
//处理异常
-
router.NoRoute(HandleNotFound)
-
router.NoMethod(HandleNotFound)
-
//router.Use(middleware.AccessLog())
-
router.Use(middleware.AccessLog()).Use(middleware.LimitMiddleware())
-
router.Use(Recover)
-
-
// 路径映射
-
articlec:=controller.NewArticleController()
-
router.GET("/article/getone/:id", articlec.GetOne);
-
router.GET("/article/list", articlec.GetList);
-
return router
-
}
-
-
//404
-
func HandleNotFound(c *gin.Context) {
-
global.Logger.Errorf("handle not found: %v", c.Request.RequestURI)
-
//global.Logger.Errorf("stack: %v",string(debug.Stack()))
-
result.NewResult(c).Error(404,"资源未找到")
-
return
-
}
-
-
//500
-
func Recover(c *gin.Context) {
-
defer func() {
-
if r := recover(); r != nil {
-
//打印错误堆栈信息
-
//log.Printf("panic: %v\n", r)
-
global.Logger.Errorf("panic: %v", r)
-
//log stack
-
global.Logger.Errorf("stack: %v",string(debug.Stack()))
-
//print stack
-
debug.PrintStack()
-
//return
-
result.NewResult(c).Error(500,"服务器内部错误")
-
}
-
}()
-
//继续后续接口调用
-
c.Next()
-
}
6,其他相关代码可访问github.com
五,测试效果
-
root@ku:/data/liuhongdi/digv14# ab -n 50 -c 50 http://127.0.0.1:8000/article/list
-
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
-
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
-
Licensed to The Apache Software Foundation, http://www.apache.org/
-
-
Benchmarking 127.0.0.1 (be patient).....done
-
-
-
Server Software:
-
Server Hostname: 127.0.0.1
-
Server Port: 8000
-
-
Document Path: /article/list
-
Document Length: 821 bytes
-
-
Concurrency Level: 50
-
Time taken for tests: 0.056 seconds
-
Complete requests: 50
-
Failed requests: 43
-
(Connect: 0, Receive: 0, Length: 43, Exceptions: 0)
-
Non-2xx responses: 43
-
Total transferred: 14441 bytes
-
HTML transferred: 7897 bytes
-
Requests per second: 894.84 [#/sec] (mean)
-
Time per request: 55.876 [ms] (mean)
-
Time per request: 1.118 [ms] (mean, across all concurrent requests)
-
Transfer rate: 252.39 [Kbytes/sec] received
-
-
Connection Times (ms)
-
min mean[+/-sd] median max
-
Connect: 0 7 2.0 7 9
-
Processing: 10 25 9.0 25 41
-
Waiting: 3 24 9.3 24 41
-
Total: 12 31 9.5 33 46
-
-
Percentage of the requests served within a certain time (ms)
-
50% 33
-
66% 37
-
75% 38
-
80% 40
-
90% 43
-
95% 46
-
98% 46
-
99% 46
-
100% 46 (longest request)
一秒时间内,只有7次成功的访问,
和我们在配置文件中设置的CountPerSecond的值是一致的
六,查看库的版本:
-
module github.com/liuhongdi/digv14
-
-
go 1.15
-
-
require (
-
github.com/gin-gonic/gin v1.6.3
-
github.com/go-playground/universal-translator v0.17.0
-
github.com/go-playground/validator/v10 v10.2.0
-
github.com/jinzhu/gorm v1.9.16
-
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
-
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
-
github.com/magiconair/properties v1.8.4 // indirect
-
github.com/mitchellh/mapstructure v1.3.3 // indirect
-
github.com/pelletier/go-toml v1.8.1 // indirect
-
github.com/pkg/errors v0.9.1 // indirect
-
github.com/spf13/afero v1.4.1 // indirect
-
github.com/spf13/cast v1.3.1 // indirect
-
github.com/spf13/jwalterweatherman v1.1.0 // indirect
-
github.com/spf13/pflag v1.0.5 // indirect
-
github.com/spf13/viper v1.7.1
-
go.uber.org/multierr v1.6.0 // indirect
-
go.uber.org/zap v1.16.0
-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
-
golang.org/x/text v0.3.4 // indirect
-
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
-
gopkg.in/ini.v1 v1.62.0 // indirect
-
gopkg.in/yaml.v2 v2.3.0 // indirect
-
)
-
posted on 2021-01-22 11:18 ExplorerMan 阅读(573) 评论(0) 编辑 收藏 举报