最近经常看到某某主播被直播平台限流惩罚,平台给主播的流量变少,甚至直接没有流量了。这篇文章要说的是后端服务的限流惩罚,和这个主播被限流惩罚有点相似之处,又有些不同。

本文说的限流惩罚是什么样的?

服务调用者调用服务的次数超过了服务允许的最大上限,也就是达到了限流阈值,此时服务会返回一个限流错误给调用方,同时还要对调用方追加一些惩罚措施,比如限制调用方10秒钟之内都不能访问服务。

为什么被限流了还要追加惩罚?

这里边的逻辑是:调用方之所以被限流,就是因为没有合理的使用服务(当然这个合理性是由服务提供方来声明的),作为服务提供方有责任督促调用方调整服务使用方式,惩罚就是督促的一种形式。如果不规范使用服务,导致被惩罚,调用方的业务也会受到较大影响,这会促使服务使用者不得不考虑改进访问服务的行为。

导致不合理调用服务的一个原因可能是调用方程序错误,比如陷入了死循环,不停的访问服务,这时候追加限流惩罚就很有必要,避免服务端承受大量无意义的请求,避免引起负载过重的问题。

还有一种是设计上的考量,比如频繁的查询用户信息导致被限流惩罚,服务提供者的目的可能是要求服务调用方对用户信息进行缓存,而不是每次都来查询服务,给服务带来较大的压力。

如何实现限流惩罚?

一个很自然的思路就是:当限流被触发时,标记调用方并设置一个过期时间,如果调用方在过期时间内来访问则直接返回错误,如果调用方在过期时间后来访问则恢复进行限流计数。这样也可以进一步降低服务端的限流处理消耗。


服务限流惩罚是怎么一回事_缓存

下面来看一下具体的实现,在 FireflySoft.RateLimit 中进程内限流和Redis限流都支持进行限流惩罚。

定义限流惩罚

因为所有的限流算法都可能需要支持限流惩罚,所以在限流规则的基类中定义了一个字段:LockSeconds,表示在触发限流后需要锁定的秒数,即触发限流后调用方不能继续访问服务的时间长度。

public abstract class RateLimitRule
{
    /// <summary>
    /// The number of seconds locked after triggering rate limiting. 0 means not locked
    /// </summary>
    public int LockSeconds { get; set; }
}

发起限流惩罚

这里以进程内限流为例,触发限流后会添加一个缓存项,并设置缓存的过期时间为上文定义的LockSeconds。

protected bool TryLock(string target, DateTimeOffset currentTime, TimeSpan expireTimeSpan)
{
    var expireTime = currentTime.Add(expireTimeSpan);
    return _cache.Add($"{target}-lock", 1, expireTime);
}

Redis发起限流惩罚的原理和这个差不多,只不过是写到Redis的KV中。想要了解的同学可以点击这里查看。

应用限流惩罚

这里还是以进程内限流为例,每次限流计数前都检查是否存在限流惩罚缓存项,如果存在则直接返回错误。

protected bool CheckLocked(string target)
{
    return _cache.Get($"{target}-lock") == null ? false : true;
}

好了,以上就是这篇文章的主要内容了。

如果你对 FireflySoft.RateLimit 有一点兴趣,欢迎访问我的Github:github.com/bosima/Fire… 。