Ventana de tiempo fija
Al desarrollar un componente limitante actual, necesitamos contar la cantidad de solicitudes dentro de un intervalo de tiempo, como en minutos. La llamada ventana de tiempo fija es para obtener el minuto dentro del cual cae la solicitud actual en función de la función de tiempo. Al contar, solo nos enfocamos en el número dentro del minuto actual, es decir, [0s, 60s]. Porque el tráfico no es uniforme, Aparecerá, superando el umbral entre dos minutos, llegaron 150 solicitudes en 1 minuto y 50 segundos y 150 solicitudes llegaron en 2 minutos y 10 segundos. Si el umbral que establecimos es 200, esto excede el umbral, que causará peligros ocultos al sistema.
La característica principal de una ventana de tiempo fija es que la ventana de muestreo salta directamente del minuto actual al minuto siguiente.
ventana de tiempo deslizante
Si llega una solicitud, retrocedemos un minuto según el tiempo de la solicitud y luego contamos el número de solicitudes en este intervalo para determinar si excede el umbral.Esta es la ventana deslizante, es decir, la ventana se desliza lentamente. Obviamente, este método no es eficiente y requiere estadísticas en todo momento.
Haga un compromiso y divida un minuto en 10 ventanas pequeñas. Cada solicitud cae en una de las ventanas pequeñas. Cada vez se mueve en unidades de ventanas pequeñas y el número de solicitudes se cuenta en la ventana pequeña en tiempo real. De esta manera , solo 10 Las ventanas se suman y se comparan con el umbral.
Principio de implementación
Aquí puede consultar la idea de diseño de una cola circular. Una matriz con una longitud de muestreo de 10 se utiliza como ventana pequeña. Un cursor apunta a la última ventana (la última vez). Si la ventana se desliza, el cursor se mueve a una posición específica.
Dado que la llegada de solicitudes no es continua, Front
el movimiento puede ser irregular.
Cada vez que se desliza una pequeña ventana hacia adelante, significa que se agrega una a la cabeza y una se descarta en la cola. Si ocurre un salto, se descartan varias en la cola y se agrega 0 en el medio. Es decir, a partir del segundo círculo, los lugares atravesados por Front se pondrán a 0.
Una simulación simple es la siguiente: 9
donde el valor de la primera fila es Front
, deslícese hacia la derecha y llegue a la segunda fila, y así sucesivamente. Hay un salto en la cuarta línea.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | X | |||||
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | X | y | |||||
5 | 6 | 7 | 8 | 9 | X | y | 0 | 0 | z | |||||
ordenado | ||||||||||||||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |||||
X | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |||||
X | y | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |||||
X | y | 0 | 0 | z | 5 | 6 | 7 | 8 | 9 |
Algoritmos y pruebas
package plugin
import (
"log"
"time"
)
// 滑动窗口
type WindowLeapArray struct {
Arr []int // 窗口数据
Front int // 游标
FrontTime int64 // 游标最新时间
WindowStatus bool // 窗口状态,true 为 拒绝访问
}
func NewWindowLeapArray(windowNum int) *WindowLeapArray {
return &WindowLeapArray{
Arr: make([]int, windowNum),
}
}
// Check 限流计算
func (w *WindowLeapArray) Check(threshold int) bool {
timenow := time.Now()
start := w.FrontTime
frontTimeLeft := timenow.UnixMilli() - timenow.UnixMilli()%100
index := (timenow.UnixMilli() - 1000*timenow.Unix()) / 100
if w.FrontTime == 0 {
// 记为小窗口的左侧时间 1694678187869 -> 1694678187800
w.FrontTime = frontTimeLeft
w.Front = int(index)
w.Arr[w.Front]++
log.Println(timenow.UnixMilli(), start, (timenow.UnixMilli()-start)/100, w.Arr)
return true
}
// 时间差
gaptime := (timenow.UnixMilli() - w.FrontTime)
if gaptime < 100 {
// 同一小窗口
if w.WindowStatus {
log.Println(timenow.UnixMilli(), start, (timenow.UnixMilli()-start)/100, w.Arr)
return false
}
// 统计
var sum int
for _, v := range w.Arr {
sum = sum + v
}
if sum >= threshold {
w.WindowStatus = true
log.Println(timenow.UnixMilli(), start, (timenow.UnixMilli()-start)/100, w.Arr)
return false
} else {
w.Arr[w.Front]++
}
} else {
// 滑动,采用环形数组
// 可能存在跳跃
w.WindowStatus = false
w.FrontTime = frontTimeLeft
gap := gaptime / 100
if gap >= 10 {
for i := 0; i < 10; i++ {
w.Arr[i] = 0
}
} else {
for i := 1; i <= int(gap); i++ {
tmp := w.Front + i
if tmp >= 10 {
tmp = tmp - 10
}
w.Arr[tmp] = 0
}
}
w.Front = int(index)
// 统计
var sum int
for _, v := range w.Arr {
sum = sum + v
}
if sum >= threshold {
w.WindowStatus = true
log.Println(timenow.UnixMilli(), start, (timenow.UnixMilli()-start)/100, w.Arr)
return false
} else {
w.Arr[w.Front] = 1
}
}
log.Println(timenow.UnixMilli(), start, (timenow.UnixMilli()-start)/100, w.Arr)
return true
}
prueba de unidad
func TestFun(t *testing.T) {
w := plugin.NewWindowLeapArray(10)
for i := 0; i < 30; i++ {
re := w.GlobalCheck()
log.Println(re)
n := util.RandInt(30, 3000)
time.Sleep(time.Duration(n) * time.Millisecond)
}
}
producción
2023/09/15 11:48:09 1694749689568 0 16947496895 [0 0 0 0 0 1 0 0 0 0]
2023/09/15 11:48:09 true
2023/09/15 11:48:11 1694749691250 1694749689500 17 [0 0 1 0 0 0 0 0 0 0]
2023/09/15 11:48:11 true
2023/09/15 11:48:12 1694749692007 1694749691200 8 [1 0 1 0 0 0 0 0 0 0]
2023/09/15 11:48:12 true
2023/09/15 11:48:12 1694749692083 1694749692000 0 [2 0 1 0 0 0 0 0 0 0]
2023/09/15 11:48:12 true
2023/09/15 11:48:13 1694749693857 1694749692000 18 [0 0 0 0 0 0 0 0 1 0]
2023/09/15 11:48:13 true
2023/09/15 11:48:14 1694749694213 1694749693800 4 [0 0 1 0 0 0 0 0 1 0]
2023/09/15 11:48:14 true
2023/09/15 11:48:15 1694749695227 1694749694200 10 [0 0 1 0 0 0 0 0 0 0]
2023/09/15 11:48:15 true
2023/09/15 11:48:15 1694749695388 1694749695200 1 [0 0 1 1 0 0 0 0 0 0]
2023/09/15 11:48:15 true
2023/09/15 11:48:16 1694749696076 1694749695300 7 [1 0 1 1 0 0 0 0 0 0]
2023/09/15 11:48:16 true
2023/09/15 11:48:16 1694749696590 1694749696000 5 [1 0 0 0 0 1 0 0 0 0]
2023/09/15 11:48:16 true
2023/09/15 11:48:18 1694749698828 1694749696500 23 [0 0 0 0 0 0 0 0 1 0]
2023/09/15 11:48:18 true
2023/09/15 11:48:20 1694749700913 1694749698800 21 [0 0 0 0 0 0 0 0 0 1]
2023/09/15 11:48:20 true
2023/09/15 11:48:22 1694749702052 1694749700900 11 [1 0 0 0 0 0 0 0 0 0]
2023/09/15 11:48:22 true
2023/09/15 11:48:23 1694749703076 1694749702000 10 [1 0 0 0 0 0 0 0 0 0]
2023/09/15 11:48:23 true
2023/09/15 11:48:23 1694749703422 1694749703000 4 [1 0 0 0 1 0 0 0 0 0]
2023/09/15 11:48:23 true
2023/09/15 11:48:23 1694749703781 1694749703400 3 [1 0 0 0 1 0 0 1 0 0]
2023/09/15 11:48:23 true
2023/09/15 11:48:23 1694749703990 1694749703700 2 [1 0 0 0 1 0 0 1 0 1]
2023/09/15 11:48:23 true
2023/09/15 11:48:26 1694749706029 1694749703900 21 [1 0 0 0 0 0 0 0 0 0]
2023/09/15 11:48:26 true
2023/09/15 11:48:28 1694749708168 1694749706000 21 [0 1 0 0 0 0 0 0 0 0]
2023/09/15 11:48:28 true
2023/09/15 11:48:29 1694749709514 1694749708100 14 [0 0 0 0 0 1 0 0 0 0]
2023/09/15 11:48:29 true
2023/09/15 11:48:30 1694749710850 1694749709500 13 [0 0 0 0 0 0 0 0 1 0]
2023/09/15 11:48:30 true
......
proceso de razonamiento
La puerta de enlace debe admitir tanto la limitación de flujo global como la limitación de flujo de IP.Código completo