この記事は、golang チュートリアル コラムによって、Go でユーザーの 1 日の制限を実装する方法を紹介するために書かれたものです。困っている友人の役に立てば幸いです。
Go にユーザーの 1 日あたりの制限を実装します (たとえば、特典は 1 日に 3 回しか受け取れません)
バグ管理システムを作成し、この
PeriodLimit
を使用する場合、各テスターが 1 日に 1 つのバグのみを送信するように制限できます。仕事はずっと楽になりましたか? :P
現在、マイクロサービス アーキテクチャが非常に人気がある本質的な理由は、システム全体の複雑さを軽減し、システム リスクをサブシステムに均等に分散してシステムの安定性を最大化し、それをさまざまなサブシステムに分割することです。サブシステム導入後は、各サブシステムを独立して開発、テスト、リリースすることができ、研究開発のリズムと効率が大幅に向上します。
しかし、呼び出しリンクが長すぎる、展開アーキテクチャの複雑さが増す、さまざまなミドルウェアが分散シナリオをサポートする必要があるなどの問題も生じます。マイクロサービスの正常な動作を保証するには、サービス ガバナンスが不可欠であり、これには通常、電流制限、ダウングレード、サーキット ブレーカーが含まれます。
電流制限とは、負荷制限を超えてシステムがダウンすることを避けるために、インターフェイス呼び出しの頻度を制限することを指します。例:
#E コマース フラッシュ セール シナリオ
#さまざまな販売者の API の現在の制限
一般的に使用される電流制限アルゴリズムは次のとおりです。
この記事では主に固定時間ウィンドウ電流制限アルゴリズムについて説明します。
ある時点から、各リクエストのリクエスト数は 1 になります。同時に、リクエストの数が 1 であるかどうかを判断します。現在の時間枠内のリクエストは制限を超えています。制限を超えた場合、リクエストは拒否されます。次の時間枠がリクエストの待機を開始すると、リクエストはクリアされます。
メリット
簡単これは効率的であり、ユーザーが 1 日に記事を 10 件しか投稿できない、SMS 認証コードを 5 回しか送信できない、ログインを 5 回しか試行できないなど、制限されたシナリオに特に適しています。このようなシナリオは実際には非常に一般的です。仕事。
欠点
固定時間ウィンドウ電流制限の欠点は、クリティカル セクションの要求バースト シナリオを処理できないことです。
現在の制限が 1 秒あたり 100 リクエストで、ユーザーが 500 ミリ秒の中間から開始して 1 秒以内に 200 リクエストを開始したとします。この時点で、200 リクエストはすべて通過できます。これは、電流を 1 秒あたり 100 回に制限するという予想と矛盾します。根本的な原因は、電流制限の粒度が粗すぎることです。
##core/limit/periodlimit.goredis lua スクリプト:Go-zero は、redis の有効期限を使用して、固定時間枠をシミュレートします。
固定時間ウィンドウ電流リミッターの定義-- KYES[1]:限流器key-- ARGV[1]:qos,单位时间内最多请求次数-- ARGV[2]:单位限流窗口时间-- 请求最大次数,等于p.quotalocal limit = tonumber(ARGV[1])-- 窗口即一个单位限流周期,这里用过期模拟窗口效果,等于p.permitlocal window = tonumber(ARGV[2])-- 请求次数+1,获取请求总数local current = redis.call("INCRBY",KYES[1],1)-- 如果是第一次请求,则设置过期时间并返回 成功if current == 1 then redis.call("expire",KYES[1],window) return 1-- 如果当前请求数量小于limit则返回 成功elseif current limit则返回 失败else return 0endログイン後にコピー
たとえば、クォータ = 5 の場合、実際のクォータは 5.4.3.2.1 になる可能性があり、定期的な変更を示しますalign パラメーターに注意してください。 = true の場合、リクエストの上限は定期的に変更されます。type ( // PeriodOption defines the method to customize a PeriodLimit. // go中常见的option参数模式 // 如果参数非常多,推荐使用此模式来设置参数 PeriodOption func(l *PeriodLimit) // A PeriodLimit is used to limit requests during a period of time. // 固定时间窗口限流器 PeriodLimit struct { // 窗口大小,单位s period int // 请求上限 quota int // 存储 limitStore *redis.Redis // key前缀 keyPrefix string // 线性限流,开启此选项后可以实现周期性的限流 // 比如quota=5时,quota实际值可能会是5.4.3.2.1呈现出周期性变化 align bool } )ログイン後にコピー
実際には、電流制限ロジックは上記 lua スクリプトが実装されていますが、戻り値0: Redis の障害やオーバーロードなどのエラーを示していることに注意してください
// Take requests a permit, it returns the permit state. // 执行限流 // 注意一下返回值: // 0:表示错误,比如可能是redis故障、过载 // 1:允许 // 2:允许但是当前窗口内已到达上限 // 3:拒绝 func (h *PeriodLimit) Take(key string) (int, error) { // 执行lua脚本 resp, err := h.limitStore.Eval(periodScript, []string{h.keyPrefix + key}, []string{ strconv.Itoa(h.quota), strconv.Itoa(h.calcExpireSeconds()), }) if err != nil { return Unknown, err } code, ok := resp.(int64) if !ok { return Unknown, ErrUnknownCode } switch code { case internalOverQuota: return OverQuota, nil case internalAllowed: return Allowed, nil case internalHitQuota: return HitQuota, nil default: return Unknown, ErrUnknownCode } }
// 计算过期时间也就是窗口时间大小 // 如果align==true // 线性限流,开启此选项后可以实现周期性的限流 // 比如quota=5时,quota实际值可能会是5.4.3.2.1呈现出周期性变化 func (h *PeriodLimit) calcExpireSeconds() int { if h.align { now := time.Now() _, offset := now.Zone() unix := now.Unix() + int64(offset) return h.period - int(unix%int64(h.period)) } return h.period }
go-zero
およびstar の使用を歓迎します私たちを応援してください!
以上がGo でユーザーの 1 日あたりの制限を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。