6、RPC框架解析:gRPC限流

6.1、简介

​ 在流量突增的情况下,为了保证服务的可用性,能做些什么呢?例如活动运营平台,平时流量不是很多,3台机器就可以解决。但是突然上线了一个奖励非常大的奖品,3台服务均到达高负载的状态,直接造成雪崩效应,应对这种突发流量,为了保证服务的可用性,引入一个新的概念,服务限流。

6.2、限流算法

​ 什么是服务限流?其实就是字面意思,限流请求的流量,达到一个快速拒绝的效果,保证服务的可用性,尽管会有部分用户请求失败,但是比全部用户不能访问,还是更友好一些。

限流为了限制单位时间内最大的处理速度。保证在服务不被压垮的情况下,以最大速率处理请求。以下场景可能会触发限流

  • 拒绝服务,感知流量暴增的情况下,限制一些高频请求的客户端。
  • 延时处理,缓冲队列中积压了大量的请求,并且已经达到队列上限。此时应该做限流处理。
  • 服务降级,cpu等资源负载,此时应该触发限流。

主流的限流算法为以下几种:

  • 计数器
  • 队列
  • 漏斗算法
  • 令牌通算法

关于更详细的介绍请看之前关于弹性服务设计的总结:分布式之弹性:服务限流

6.3、实践

使用我们的demo工程,demo,这里的限流器我们直接使用,golang.org/x/time/rate

创建限流器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type greeter struct {
cli *clientv3.Client
limiter *rate.Limiter
}

func NewGreeter() (*greeter, error) {
// 初始化etcd客户端
cli, _ := clientv3.New(clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
DialTimeout: time.Second * 3,
})
imp := &greeter{
cli: cli,
// 初始化限流器,令牌桶大小为100,每秒往桶里放10个令牌
limiter:rate.NewLimiter(100,10),
}

return imp, nil
}

限流触发检查:

1
2
3
4
5
6
7
8
9
10
func (g *greeter) SayHello(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
// 检查当前是否允许该事件通过,使用Wait可以是服务在这里发生等待。
if !g.limiter.Allow(){
return nil,errors.New("service is busy, please try again later")
}
rsp := &helloworld.HelloReply{
Message: fmt.Sprintf("hello:%s", req.Name),
}
return rsp, nil
}

6.4、总结

​ 开源的限流组件有很多,重要的是了解他们的实现原理,以及各个算法的应用场景,例如:计数器就适合在集群的场景下使用,配合滑动窗口,令牌通和漏斗提供了比较精准的限流策略,漏斗是让服务以恒定速率处理请求,但是令牌通算法是允许一定程度的流量突发。

​ 具体使用什么场景,还是应该结合业务需求来看,我们平时使用较多的就是令牌通算法。