服务限流场景
在高并发大流量系统中,由于并发大造成服务资源不足,负载过高,进而引发致一系列问题,这里的流量一般都是突发性的,由于系统准备不足,很难短期扩容来应对 ,进行限流是最常用的手段,所以说限流也是服务稳定性治理重要的手段。
限流可能发生在多个层面:
1.用户网络层:突发的流量场景如热点事件流量(秒杀事件、热门抢购,微博热搜),恶意刷流,竞对爬虫等。
2.内部应用层:上游服务的异常调用,脚本异常请求,失败重试策略造成流量突发。
实现限流方案
常用的限流方法主要有三种:计数器算法,漏斗桶算法,令牌桶算法。
1.计算器限流
1.1 实现原理
设计限流条件,如根据用户id/商户id/IP/UUID+请求url作为限流对象,对限流对象的每次流量访问进行全局计数,设置限流阈值(1000次/秒,10000/分钟),如果统计时间窗口期内达到阈值就进行限流。
对单机限流来说,使用全局内存计数即可,但对分布式系统需要有一个公共存储计数,redis是最佳存储方案,且redis的incr能保障原子性操作。
1.2 代码实现
//@param key string object for rate limit such as uid/ip+url//@param fillInterval time.Duration such as 1*time.Second//@param limitNum max int64 allowed number per fillInterval//@return whether reach rate limit, false means reach.func fixedWindowRateLimit(key string, fillInterval time.Duration, limitNum int64) bool {
//current tick time window tick := int64(time.Now().Unix() / int64(fillInterval.Seconds())) currentKey := fmt.Sprintf("%s_%d_%d_%d", key, fillInterval, limitNum, tick) startCount := 0 _, err := client.SetNX(currentKey, startCount, fillInterval).Result() if err != nil {
panic(err) } //number in current time window quantum, err := client.Incr(currentKey).Result() if err != nil {
panic(err) } if quantum > limitNum {
return false } return true}
完整代码参见:
https://github.com/skyhackvip/ratelimit/blob/master/fixedwindow.go
测试代码:
func test1() {
for i := 0; i < 10; i++ {
go func() {
rs := fixedWindowRateLimit("test1", 1*time.Second, 5) fmt.Println("result is:", rs) }() } }
测试执行结果:
根据执行结果可以看到&#x