golang接口IP限流,IP黑名单,IP白名单的实例
来源:脚本之家
时间:2023-01-07 11:59:20 166浏览 收藏
怎么入门Golang编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《golang接口IP限流,IP黑名单,IP白名单的实例》,涉及到限流、IP,有需要的可以收藏一下
增加中间件
可以选择普通模式和LUA脚本模式,建议选择普通模式,实际上不需要控制的那么精确。
package Middlewares import ( "github.com/gin-gonic/gin" "strconv" "time" "voteapi/pkg/app/response" "voteapi/pkg/gredis" "voteapi/pkg/util" ) const IP_LIMIT_NUM_KEY = "ipLimit:ipLimitNum" const IP_BLACK_LIST_KEY = "ipLimit:ipBlackList" var prefix = "{gateway}" var delaySeconds int64 = 60 // 观察时间跨度,秒 var maxAttempts int64 = 10000 // 限制请求数 var blackSeconds int64 = 0 // 封禁时长,秒,0-不封禁 func GateWayPlus() gin.HandlerFunc { return func(c *gin.Context) { path := c.FullPath() clientIp := c.ClientIP() // redis配置集群时必须 param := make(map[string]string) param["path"] = path param["clientIp"] = clientIp if !main(param) { c.Abort() response.JsonResponseError(c, "当前IP请求过于频繁,暂时被封禁~") } } } func main(param map[string]string) bool { // 预知的IP黑名单 var blackList []string if util.InStringArray(param["clientIp"], blackList) { return false } // 预知的IP白名单 var whiteList []string if util.InStringArray(param["clientIp"], whiteList) { return false } blackKey := prefix + ":" + IP_BLACK_LIST_KEY limitKey := prefix + ":" + IP_LIMIT_NUM_KEY curr := time.Now().Unix() item := util.Md5(param["path"] + "|" + param["clientIp"]) return normal(blackKey, limitKey, item, curr) } // 普通模式 func normal(blackKey string, limitKey string, item string, time int64) (res bool) { if blackSeconds > 0 { timeout, _ := gredis.RawCommand("HGET", blackKey, item) if timeout != nil { to, _ := strconv.Atoi(string(timeout.([]uint8))) if int64(to) > time { // 未解封 return false } // 已解封,移除黑名单 gredis.RawCommand("HDEL", blackKey, item) } } l, _ := gredis.RawCommand("HGET", limitKey, item) if l != nil { last, _ := strconv.Atoi(string(l.([]uint8))) if int64(last) >= maxAttempts { return false } } num, _ := gredis.RawCommand("HINCRBY", limitKey, item, 1) if ttl, _ := gredis.TTLKey(limitKey); ttl == int64(-1) { gredis.Expire(limitKey, int64(delaySeconds)) } if num.(int64) >= maxAttempts && blackSeconds > 0 { // 加入黑名单 gredis.RawCommand("HSET", blackKey, item, time+blackSeconds) // 删除记录 gredis.RawCommand("HDEL", limitKey, item) } return true } // LUA脚本模式 // 支持redis集群部署 func luaScript(blackKey string, limitKey string, item string, time int64) (res bool) { script := ` local blackSeconds = tonumber(ARGV[5]) if(blackSeconds > 0) then local timeout = redis.call('hget', KEYS[1], ARGV[1]) if(timeout ~= false) then if(tonumber(timeout) > tonumber(ARGV[2])) then return false end redis.call('hdel', KEYS[1], ARGV[1]) end end local last = redis.call('hget', KEYS[2], ARGV[1]) if(last ~= false and tonumber(last) >= tonumber(ARGV[3])) then return false end local num = redis.call('hincrby', KEYS[2], ARGV[1], 1) local ttl = redis.call('ttl', KEYS[2]) if(ttl == -1) then redis.call('expire', KEYS[2], ARGV[4]) end if(tonumber(num) >= tonumber(ARGV[3]) and blackSeconds > 0) then redis.call('hset', KEYS[1], ARGV[1], ARGV[2] + ARGV[5]) redis.call('hdel', KEYS[2], ARGV[1]) end return true ` result, err := gredis.RawCommand("EVAL", script, 2, blackKey, limitKey, item, time, maxAttempts, delaySeconds, blackSeconds) if err != nil { return false } if result == int64(1) { return true } else { return false } }
补充:golang实现限制每秒多少次的限频操作
前言
一些函数的执行可能会限制频率,比如某个api接口要求每秒最大请求30次。下面记录了自己写的限频和官方的限频
代码
// 加锁限频,输出次数大概率小于最大值 func ExecLimit(lastExecTime *time.Time, l *sync.RWMutex ,maxTimes int, perDuration time.Duration, f func()) { l.Lock() defer l.Unlock() // per times cost time(s) SecondsPerTimes := float64(perDuration) / float64(time.Second) / float64(maxTimes) now := time.Now() interval := now.Sub(*lastExecTime).Seconds() if interval使用
func TestExecLimit(t *testing.T) { runtime.GOMAXPROCS(runtime.NumCPU()) go func() { var lastExecTime time.Time var l sync.RWMutex for { ExecLimit(&lastExecTime, &l, 10, time.Second, func() { fmt.Println("do") }) } }() select { case输出:
一秒内输出了
如何在多节点服务中限制频
上述使用,定义在某个服务节点的全局变量lastExecTime仅仅会对该服务的函数f()操作限频,如果在负载均衡后,多个相同服务的节点,对第三方的接口累计限频,比如三个服务共同拉取第三方接口,合计限频为30次/s.
则,必须将lastExecTime的获取,从redis等共享中间件中获取,而不应该从任何一个单点服务获取。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持golang学习网。如有错误或未考虑完全的地方,望不吝赐教。
文中关于golang的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《golang接口IP限流,IP黑名单,IP白名单的实例》文章吧,也可关注golang学习网公众号了解相关技术文章。
声明:本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
-
290 收藏
-
376 收藏
-
398 收藏
-
237 收藏
-
217 收藏
最新阅读
更多>
-
419 收藏
-
234 收藏
-
155 收藏
-
457 收藏
-
309 收藏
-
225 收藏
课程推荐
更多>
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习