SoFunction
Updated on 2025-03-05

Examples of golang interface IP current limit, IP blacklist, and IP whitelist

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.