我们有一个服务以类似于SideCar的方式与应用程序一起运行,SideCar通过UnixDomainSocket与应用程序通信。为了方便用户,在开发过程中不需要在自己的开发环境中运行SideCar。我使用socat将UDS映射到开发环境机器上的端口。这样用户在开发的时候就可以直接通过这个TCP端口来测试服务,而不用开一个SideCar来使用UDS。因为大家都要用这个地址进行开发,所以存在相互影响的问题。虽然性能还不错,几十万QPS不是问题,但总有傻子拿来做压力测试,跑满资源,影响别人。我在说明书上用大红字写了这是开发测试用的,不能做压力测试。一些视力不好的同事会强制进行压力测试。我时不时地要解释一下,并礼貌地要求我的同事不要再这样做了。最近真的好累研究了一下,直接给这个端口加上perIP的限速,效果还不错。该方法是从使用iptables[1]的Per-IP速率限制中学习的。该公司提供多租户SaaS服务,存在类似问题:一些异常用户滥用服务,因为滥用发生在连接建立阶段,还没有进入业务代码,所以无法从应用上限速level,通过iptables找到解决办法。详细的实现方法可以参考这篇文章。iptables本身是无状态的,每个传入的数据包独立判断规则。ratelimit显然是一个有状态的规则,所以使用了module:hashlimit。(原文中也用到了Conntrack,他只想限制新建连接的速度,不限制已建立连接的速度。因为这个应用程序内部可以控制,但是我想在这里限制所有数据包的速率,所以我不需要使用这个模块)完整的命令如下:$iptables--new-chainSOCAT-RATE-LIMIT$iptables--appendSOCAT-RATE-LIMIT\--matchhashlimit\--hashlimit-modesrcip\--hashlimit-upto50/sec\--hashlimit-burst100\--hashlimit-nameconn_rate_limit\--jumpACCEPT$iptables--appendSOCAT-RATE-LIMIT--jumpDROP$iptables-IINPUT-ptcp--dport1234--jumpSOCAT-RATE-LIMIT第一行新建iptablesChain,设置速率限制;第二行处理如果在ratelimit限制内,接受包裹;否则跳到前三行,直接DROPpackage;最后,将新的Chain添加到INPUT中,以限制该端口的流量。限速算法主要有两个参数:--hashlimit-upto实际上是1s可以进多少包,50/sec是20ms的一个包;那么如何在10毫秒内发送10个数据包,并且以后不再发送,怎么办?这在测试场景中也是比较常见的,不能要求用户一直匀速发送。所以使用--hashlimit-burst。字面意思是瞬间可以发送多少个数据包,但实际上可以理解为这个参数是可用的信用。两个指标可以一起理解,就是一开始每个IP都会有burstcredit,每个IP发送的包都会在burst中占用credit,用完之后发送的包直接DROP。这个credit会一直以upto的速度增加,但是最多会增加到burst(初始值),然后用掉或者丢掉。比如--hashlimit-upto50/sec--hashlimit-burst20,某个IP以每秒一个包的恒定速率发送,最后会接受多少个包?答案是70,前20ms,所有包都会被接受,因为--hashlimit-burst是20,所以初始信用是20,这个用完之后,需要依赖--hashlimit--upto50/sec到每20ms获得一个数据包信用。所以每20ms一个是可以接受的。这是限速后的效果,非常明显:
