限流策略的一些摸索
August 2022
git前端代理服务的特点和相应的限流策略,首先过一遍web服务常用的限流方式
限流尝试-backlog
实际上,所有tcp连接都有名为backlog的限流策略
操作系统维持了syn queue和accept queue两个队列来处理tcp握手,简单来讲,未完成握手的连接在syn queue;完成握手的在accept queue等待应用取用

backlog由操作系统内核实现,我们在应用中能干涉的程度很有限(调整队列长度,调整队列溢出时的操作等)
通常可以调整accept queue队列的长度来限流,但backlog在accept queue队列溢出后只有两种操作可以选择
- 丢弃ACK,服务器发送ACK_SYN要求客户端重传,直到进入
accept queue或达到重试限制超时 - 发送RESET,关闭连接
显而易见,上述两种处理对于用户来说均称不上友好,尤其是发送RESET,客户端无法分辨端口未被监听和'accept queue'队列已满这两种情况
我们在绝大多数情况下看到
拒绝连接都会下意识的认为服务挂了,需要给用户响应服务器忙,请稍后重试这类更友好的信息
限流尝试-漏桶和令牌桶
漏桶和令牌桶是web服务常用的两种限流算法
两者都是桶,代表他们都有容量这一概念
漏桶的容量指可以堆积等待处理的请求数量;而令牌桶的容量指同一时刻最多放行的请求数量
漏桶通常使请求以一定的速率通过,超出这个速率的请求会被放在桶里,桶满了便拒绝后续请求

令牌桶相对于漏桶特殊一些,它以一定的速率生产令牌并放进桶中,桶满了之后新生成的令牌便会丢弃,请求到达之后,要从令牌桶里拿到一个令牌才可以继续往下走,否则便拒绝/等待。相对于漏洞,它允许一定程度的突发流量

这两种方式都可以有限控制请求进入应用的速率,对于大多数web应用来说,漏桶和令牌桶取其一都是够用的
git前端代理服务
对于git代理服务来说,每一个请求耗时较长且资源消耗大,我们相对于控制请求进入应用的速率,更关注应用正在处理的请求的数量。有鉴于此,前一节介绍的漏桶和令牌桶都无法满足我们的需求
在git前端代理服务这一场场景中,使用漏桶和令牌桶可能会导致大量请求堆积在服务中,严重时互相抢占资源导致整个服务卡顿
流量整形ex
参照令牌桶的思路,根据我们的具体需求设计限流算法,它需要具有以下特征
- 每一个请求都需要获取一个token才可以继续执行,否则必须
等待 - 每处理完一个请求
- 若没有请求处于
等待状态便交回令牌\ - 否则唤醒一个处于
等待状态的请求进行处理 - 处于
等待状态的请求过多便直接拒绝新请求 - 请求
等待时间超过闸值便拒绝请求
现实场景
这种流量整形策略类似于加油站,必须等待有加油机空闲时才可以放车子进来加油,否则就排队
队伍快堵到马路的时候,新来的就不准继续排了
排队过去太久还没轮到自己加油便回家等人少了再来
总结
流量整形是大多数web服务都需要的基本特性,对于git前端代理服务而言,如果没有流量整形,仓库端很可能由于无法承受过量的git推拉而挂掉
但在很多时候,git相关服务不能直接使用通用的web解决方案,根据服务特点开发适用于自身需求的解决方案便成为了代码托管平台开发者的基本要求