Add middleware
You can choose normal mode and LUA script mode. It is recommended to choose normal mode, which does not actually require control as precise.
package Middlewares import ( "/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 // Observe the time span, secondsvar maxAttempts int64 = 10000 // Limit the number of requestsvar blackSeconds int64 = 0 // Time limit, seconds, 0-Not blockedfunc GateWayPlus() { return func(c *) { path := () clientIp := () // Redis must be configured when configuring the cluster param := make(map[string]string) param["path"] = path param["clientIp"] = clientIp if !main(param) { () (c, "The current IP request is too frequent and temporarily banned~") } } } func main(param map[string]string) bool { // Predicted IP blacklist var blackList []string if (param["clientIp"], blackList) { return false } // Predicted IP whitelist var whiteList []string if (param["clientIp"], whiteList) { return false } blackKey := prefix + ":" + IP_BLACK_LIST_KEY limitKey := prefix + ":" + IP_LIMIT_NUM_KEY curr := ().Unix() item := util.Md5(param["path"] + "|" + param["clientIp"]) return normal(blackKey, limitKey, item, curr) } // Normal modefunc normal(blackKey string, limitKey string, item string, time int64) (res bool) { if blackSeconds > 0 { timeout, _ := ("HGET", blackKey, item) if timeout != nil { to, _ := (string(timeout.([]uint8))) if int64(to) > time { // Unlocked return false } // Unblocked, blacklist removed ("HDEL", blackKey, item) } } l, _ := ("HGET", limitKey, item) if l != nil { last, _ := (string(l.([]uint8))) if int64(last) >= maxAttempts { return false } } num, _ := ("HINCRBY", limitKey, item, 1) if ttl, _ := (limitKey); ttl == int64(-1) { (limitKey, int64(delaySeconds)) } if num.(int64) >= maxAttempts && blackSeconds > 0 { // Add to blacklist ("HSET", blackKey, item, time+blackSeconds) // Delete records ("HDEL", limitKey, item) } return true } // LUA script mode// Support redis cluster deploymentfunc luaScript(blackKey string, limitKey string, item string, time int64) (res bool) { script := ` local blackSeconds = tonumber(ARGV[5]) if(blackSeconds > 0) then local timeout = ('hget', KEYS[1], ARGV[1]) if(timeout ~= false) then if(tonumber(timeout) > tonumber(ARGV[2])) then return false end ('hdel', KEYS[1], ARGV[1]) end end local last = ('hget', KEYS[2], ARGV[1]) if(last ~= false and tonumber(last) >= tonumber(ARGV[3])) then return false end local num = ('hincrby', KEYS[2], ARGV[1], 1) local ttl = ('ttl', KEYS[2]) if(ttl == -1) then ('expire', KEYS[2], ARGV[4]) end if(tonumber(num) >= tonumber(ARGV[3]) and blackSeconds > 0) then ('hset', KEYS[1], ARGV[1], ARGV[2] + ARGV[5]) ('hdel', KEYS[2], ARGV[1]) end return true ` result, err := ("EVAL", script, 2, blackKey, limitKey, item, time, maxAttempts, delaySeconds, blackSeconds) if err != nil { return false } if result == int64(1) { return true } else { return false } }
Supplement: golang implements a limiting frequency operation that limits how many times per second
Preface
The execution of some functions may limit the frequency, such as a certain API interface requires a maximum request of 30 times per second. The following records the frequency limits I wrote and the official frequency limits
Code
// Add locking limit frequency, the output frequency is likely to be less than the maximum valuefunc ExecLimit(lastExecTime *, l * ,maxTimes int, perDuration , f func()) { () defer () // per times cost time(s) SecondsPerTimes := float64(perDuration) / float64() / float64(maxTimes) now := () interval := (*lastExecTime).Seconds() if interval < SecondsPerTimes { ((int64((SecondsPerTimes-interval)*1000000000)) * ) } f() *lastExecTime = () } // Official, need to quote "/x/time/rate"// Basically, it can reach full value, which is better than what you writefunc ExecLimit2(l *, f func()) { go func() { (()) f() }() }
use
func TestExecLimit(t *) { (()) go func() { var lastExecTime var l for { ExecLimit(&lastExecTime, &l, 10, , func() { ("do") }) } }() select { case <-(1 * ): ("1 second is here") } } func TestExecLimit2(t *) { (()) l := (1, 30) go func() { for { ExecLimit2(l, func() { ("do") }) } }() select { case <-(1 * ): ("1 second is here") } }
Output:
Output <=10 times "do" in one second
How to limit frequency in multi-node service
The above use, the global variable lastExecTime defined in a service node will only limit the frequency of the function f() of the service. If after load balancing, multiple nodes of the same service will cumulatively limit the frequency of the third-party interface. For example, the three services jointly pull the third-party interface, and the total frequency limit is 30 times/s.
Then, the lastExecTime must be retrieved from shared middleware such as redis, and should not be retrieved from any single point of service.
The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.