一顿火锅引发对限流的思考,深夜误点!

713 阅读7分钟

前言

由于疫情的影响,在家一个月了,火锅、烤肉、烧烤、奶茶已经不认识我是谁了。每每翻到一点关于美食的东西,就馋的不行。终于等到复工,当晚就点了一个火锅外卖,在家准备解解馋。现在火锅外卖挺方便的,如果不想洗锅,可以点一次性锅,点的食材也都是切好的,我一瞬间点了牛肉,猪肉、牛肉丸、鱼丸、虾滑、毛肚、黄喉、土豆等食材。等到外卖到来,摆好所有东西,准备开吃,可是吃着吃着,我发现味道并没有想象中那么好了,难道是我一个月就已经忘记了火锅味?后来我发现是因为我把很多食材一股脑儿都放在锅里了,而在外面吃的时候会根据食材的不同来决定烫还是煮,煮多久等等。就拿出名的重庆九宫格火锅来说:

  • 中心格:温度高,就是用来烫的,适合比较容易熟的食材,如毛肚、肥牛、腰片等。

  • 十字格:温度中等,用来入味,适合下虾滑、其特色为边煮边吃等。

  • 四角格:温度低,适合长时间蒸煮需要入味的食材,比如脑花、肥肠、鱿鱼等。

针对不同的食材有需要不同的火候,这可能就是爱情中经常讲的很悬的“合适”吧。因为一次性锅比较小,所能放的食材比较少,所以很多时候我就在等肉煮熟,在等待中觉得这非常像限流的场景,由此对限流展开了一阵思考。

限流

机器在单位时间内所能承载或者处理的请求数是有限的,这个阀值取决于机器的整体性能以及代码中此次请求所要处理的逻辑。在高并发的场景下,如果不对请求进行限流,可能会出现在单位时间内大量请求涌入某台机器实例,导致大量请求在队列等待,或者导致机器资源耗尽,甚至是服务挂掉。除了防止由于流量洪峰导致服务被击垮以外,一定的限流也能保证整个集群的QPS最佳,因为如果一台机器实例挂掉,整个集群就少了一台机器处理请求,别的机器就要分担更多的请求,如果流量洪峰持续时间过长,会拖垮整个集群。我们知道请求可以被拒绝,但是服务绝对不能全部挂。所以为了保证服务的高可用,限流是必不可少的一个方案。

限流算法

目前耳熟能详的限流算法有:

  • 漏桶算法
  • 令牌桶算法

介绍这两种算法前先拟定一个场景,见下图:

我脑海中想要吃某个食材,就会去看火锅里面有没有这个食材已经被煮熟,这是我发送的一个request(假设一个食材只能被当作一个request发送),需要火锅给予我response,而火锅如果没有对应的食材,则会去食材库查看是否有对应的原材料,这个时候就是火锅向食材库进行请求。这是一个很简单的调用链。

漏桶算法

漏桶算法用上面的场景就很好理解,当火锅瞬间想要毛肚、牛肉等N个食材,也就是食材库会接收到火锅的N个请求,但是火锅在1s内只能煮Q个食材且Q<N,也就是火锅能处理的响应只有Q个,假设食材库存最多能处理的食材是M个,并且N<M,那么其实食材库是可以承受住这么多的请求的,但是当食材库在1s内把N个食材整理好返回给火锅时,火锅并不能够煮得下这么多,这个时候火锅就会溢出,反观计算机,也就是在单位时间内provider端返回响应时consumer端并不能承受住该回调洪峰,导致consumer端被击垮。

这个时候就可以用漏桶算法来解决这类问题,它可以保证火锅每一秒想要的食材只能是t个,也就是火锅的请求速率是恒定的,那么在单位时间内,食材库返回的响应也能得以控制,这样就不会造成火锅溢出,也就是服务被击垮的情况。

令牌桶算法

漏桶算法虽然可以对回调洪峰做流量整形,但是对于用户洪峰,服务依旧会有被击垮的风险,比如当我1s内想要吃r个食材时,但是r>Q,也就是火锅没有办法承受住我这么大的请求量,那么火锅依旧会被击垮,这种时候不应该用漏桶算法去限制我想要吃的数量,因为限制了我单位时间想要吃的食材数,就等同于每一个单位时间能够发出请求数量就被限制了,而在一个产品中,如果每个用户的请求速率都被限制成一样并且并不快,那这样的产品将很难被接受。

对于这种用户洪峰,令牌桶算法可以有效的对流量进行整形。还是拿火锅这个例子,上面不是说火锅在1s内只能煮Q个食材吗,那么我就把这个锅用挡板分割成Q 个空间(假设一个空间只能煮一个食材),有点类似于重庆九宫格火锅。当我瞬间想要吃X 个食材时,每个食材都从一个空间里面去拿,如果那个空间是空的,则火锅可以煮我想要的食材,如果瞬间整个锅没有空的空间,则其他请求都被拒绝,当一个空间的食材被我取走后,该空间就会被释放,又可以用来接受食材请求并且煮食材。这就是令牌桶算法。

限流粒度

除了对两个限流算法重新审视了他们的应用场景外,还对限流粒度也做了思考,从粒度上来说一次请求,无非就是一次RPC的方法调用,所以最小的粒度应该是方法级别的限流,也就是上述火锅案例中,针对我想要吃某个食材这个请求进行限流。如果对每个方法都配置不同的阀值,整个系统的维护成本就会大大增加,所以一般只是对特殊的方法采用更细的阀值配置。除了方法级别的限流外,更上一层的就是服务级别的限流,一个服务会有很多个方法,有时候我们不想要对一个一个方法进行限流,可能存在某个服务在一些商城促销活动等场景下需要限流,而这时候希望能够直接对某个服务进行限流,而并不是对服务中的方法进行一一限流,服务粒度的限流是用的最多的。再上一层就是对集群进行限流,也许是某几台机器的性能不佳,所以不能跟正常的机器配置一样的限流阀值。面向整个集群的限流一般是用作兜底的保护措施,使系统不会被突然流量洪峰击垮。

送福利区域

扫描下方二维码关注公众号【加点代码调调味】 点击菜单栏获取免费49篇的《Dubbo源码解析》系列文章