La idea y la implementación de ventana de tiempo deslizante, matriz circular, golang y limitación de corriente.

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.

Insertar descripción de la imagen aquí

Dado que la llegada de solicitudes no es continua, Frontel 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: 9donde 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

Insertar descripción de la imagen aquí

La puerta de enlace debe admitir tanto la limitación de flujo global como la limitación de flujo de IP.Código completo

Supongo que te gusta

Origin blog.csdn.net/raoxiaoya/article/details/132902323
Recomendado
Clasificación