Go-Sprachimplementierung, Benutzeranmeldung: Begrenzen Sie die Anzahl falscher Passwörter innerhalb einer Minute (Strombegrenzungsalgorithmus mit festem Zeitfenster).

1. Einleitung

Strombegrenzungen sind bei der serverseitigen Entwicklung keine Seltenheit. Das erste, was mir in den Sinn kommt, ist Taobao 双11, 12306die Szene, in der zu Neujahr Zugtickets gekauft werden, wenn man nach Hause zurückkehrt usw.

Hier ist ein einfaches Geschäftsszenario: Der Benutzer meldet sich mit seinem Konto und Passwort an. Um zu verhindern, dass Schadprogramme versuchen, Passwörter mit Brute-Force zu knacken und häufig Anmeldeanfragen aufrufen, können eine Reihe von Schutzmaßnahmen hinzugefügt werden, wie z : Hinzufügen einer Bestätigungscode-Verifizierung, die innerhalb eines festgelegten Zeitraums überprüft werden kann. Die Anzahl der Versuche der fehlerhaften Anfrage usw.

2. Gehen Sie zur Umsetzung

Verwenden Sie go-cacheOpen-Source-Warehouses für die Cache-Steuerung.

package main

import (
	"fmt"
	"github.com/patrickmn/go-cache"
	"time"
)

// 使用
func main() {
    
    
    // 设置一分钟内可以用户a可以尝试10次
	c := NewCache(10, time.Minute, time.Second)
	key := "a"
	go func() {
    
    
		ticker := time.NewTicker(time.Millisecond * 200)
		for {
    
    
			select {
    
    
			case <-ticker.C:
				fmt.Println("add")
				c.Add(key)
			}
		}
	}()

	for {
    
    
		if c.Limit(key) {
    
    
			fmt.Println("end")
			return
		}
	}
}


type Cache struct {
    
    
	c          *cache.Cache
	countLimit int
}

// cleanupInterval 定时清理缓存的时间
func NewCache(countLimit int, timeLimit, cleanupInterval time.Duration) *Cache {
    
    
	res := new(Cache)
	res.c = cache.New(timeLimit, cleanupInterval)
	res.countLimit = countLimit

	return res
}

func (this *Cache) Limit(key string) bool {
    
    
	count, ok := this.c.Get(key)
	if !ok {
    
    
		return false
	}
	curCount := count.(int)
	if curCount >= this.countLimit {
    
    
		return true
	}
	return false
}

func (this *Cache) Add(key string) {
    
    
	count, expiration, ok := this.c.GetWithExpiration(key)
	if !ok {
    
    
		this.c.SetDefault(key, 1)
		return
	}

    // 这里的过期时间重新算一下
	duration := expiration.Sub(time.Now())
	if duration <= 0 {
    
    
		return
	}

	curCount := count.(int)
	this.c.Set(key, curCount+1, duration)
	return
}

Der obige Code wird 10mal ausgegeben addund endbeendet dann das Programm.

3. Strombegrenzungsstrategie

Es gibt viele aktuelle Begrenzungsstrategien. Hier sind einige gängige. Die Auswahl sollte auf der tatsächlichen Situation basieren.

3.1 Strombegrenzung für festes Zeitfenster

Es ist sehr rau. Wenn sich innerhalb von zwei Zeitfenstern der vorherige Burst-Verkehr am Ende und der nächste Burst-Verkehr an der Spitze befindet, kann es immer noch zu einer Systemlähmung kommen.

3.2 Gleitendes Zeitfenster

Eine Verbesserung des Algorithmus für feste Zeitfenster. Nachdem der Verkehr durch den Algorithmus für gleitende Zeitfenster geformt wurde, kann garantiert werden, dass die maximal zulässige Flussgrenze in keinem Zeitfenster überschritten wird. Die Flusskurve wird glatter, was teilweise möglich ist Lösen Sie das oben genannte Problem. Das erwähnte kritische Burst-Traffic-Problem.

3.3 Token-Bucket

  1. Die Schnittstelle begrenzt die maximale Anzahl an Zugriffen innerhalb von t Sekunden auf n, dann wird alle t/n Sekunden ein Token in den Bucket gelegt;
  2. Im Bucket können maximal b Token gespeichert werden. Wenn der Token-Bucket beim Eintreffen des Tokens voll ist, wird der Token verworfen;
  3. Die Schnittstellenanforderung ruft zunächst das Token aus dem Token-Bucket ab. Wenn das Token abgerufen wird, wird die Schnittstellenanforderung verarbeitet. Wenn das Token nicht abgerufen werden kann, wird eine Strombegrenzung durchgeführt.

Token werden mit einer konstanten Rate generiert, bis der maximale Wert des Buckets erreicht ist. Von jeder Anfrage wird ein Token abgezogen und alle Token, die nicht abgerufen werden können, werden verworfen.

3.4 Undichter Eimer

Es wird erfasst, wie viel insgesamt untergebracht werden kann und Ein- und Ausgänge erfasst. Sobald der Eimer voll ist, darf er nicht mehr reingelassen werden.

4. So konfigurieren Sie sinnvolle Strombegrenzungsregeln

Strombegrenzungsregeln bestehen aus drei Teilen: Zeitgranularität, Schnittstellengranularität und maximaler Strombegrenzungswert.

Strombegrenzungsanpassung, Hot-Update (Strombegrenzung ein-/ausschalten, Strombegrenzungsregeln anpassen, Strombegrenzungsalgorithmus ersetzen).

5. Zusammenfassung

Dieser Artikel verwendet hauptsächlich ein Unternehmen, um die Strombegrenzung (festes Zeitfenster) kurz zu beschreiben, und enthält eine goVersion der Implementierung.

Es gibt viele Möglichkeiten, den Strom zu begrenzen, aber Sie müssen immer noch den für Ihr Unternehmen am besten geeigneten Weg finden. Die Schwierigkeit liegt nicht in der Implementierung, sondern darin, wie man den Strom begrenzt und wie man ihn gestaltet.

6. Referenz

Design und Überlegungen zu aktuellen Einschränkungen der InfoQ-Microservice-Schnittstelle

2022/10/18erneuern

AddMethoden und LimitMethoden werden zusammengeführt.

package main

import (
	"fmt"
	"github.com/patrickmn/go-cache"
	"time"
)

func main() {
    
    
	c := NewCache(10, time.Minute, time.Minute)
	key := "a"

	ticker := time.NewTicker(time.Millisecond * 200)
	i := 0
	for {
    
    
		select {
    
    
		case <-ticker.C:
			i++
			if c.Limit1(key) {
    
    
				fmt.Println("end:", i)
				return
			}
		}
	}
}

func (this *Cache) Limit1(key string) bool {
    
    
	count, expiration, ok := this.c.GetWithExpiration(key)
	if !ok {
    
    
		this.c.SetDefault(key, 1)
		return false
	}

	duration := expiration.Sub(time.Now())
	if duration <= 0 {
    
     // 过期了重新设置值
		this.c.SetDefault(key, 1)
		fmt.Println("过期了")
		return false
	}

	curCount := count.(int)
	if curCount >= this.countLimit {
    
    
		return true
	}
	this.c.Set(key, curCount+1, duration)
	return false
}

Ausgabeergebnis:end:11

おすすめ

転載: blog.csdn.net/DisMisPres/article/details/127305658