Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go 分布式出站http速率限制器_Go_Distributed_Rate Limiting - Fatal编程技术网

Go 分布式出站http速率限制器

Go 分布式出站http速率限制器,go,distributed,rate-limiting,Go,Distributed,Rate Limiting,我有一个微服务体系结构应用程序,它有多个服务轮询一个外部API。外部API的速率限制为每分钟600个请求。我如何使我的所有实例都低于共享的600费率限制 谷歌只为我带来了3种解决方案,其中最有希望的是: 这是三个中最有希望的一个,但我确实不知道如何设置它 它似乎只有在达到限制时才会拒绝(我的应用程序需要等待,直到它能够发出请求),并且速率中的每个函数。NewLimiter函数似乎是一个不同的导入/依赖项,我无法确定它是哪一个 有一个“软”的极限,很明显,可以让我超过这个极限。有些端点我并不介意

我有一个微服务体系结构应用程序,它有多个服务轮询一个外部API。外部API的速率限制为每分钟600个请求。我如何使我的所有实例都低于共享的600费率限制

谷歌只为我带来了3种解决方案,其中最有希望的是:

  • 这是三个中最有希望的一个,但我确实不知道如何设置它
  • 它似乎只有在达到限制时才会拒绝(我的应用程序需要等待,直到它能够发出请求),并且
    速率中的
    每个
    函数。NewLimiter
    函数似乎是一个不同的导入/依赖项,我无法确定它是哪一个
  • 有一个“软”的极限,很明显,可以让我超过这个极限。有些端点我并不介意我在几秒钟内无法访问它们,但是其他端点请求应该尽可能多地工作
目前我有一个业余解决方案。下面的代码允许我设置每分钟的限制,它在请求之间休眠,以在一分钟内分散请求。这个客户端速率限制是每个实例的,因此我必须硬编码将600个请求除以实例的数量

var semaphore = make(chan struct{}, 5)
var rate = make(chan struct{}, 10)

func init(){
    // leaky bucket
    go func() {
        ticker := time.NewTicker(100 * time.Millisecond)
        defer ticker.Stop()
        for range ticker.C {
            _, ok := <-rate
            // if this isn't going to run indefinitely, signal
            // this to return by closing the rate channel.
            if !ok {
                return
            }
        }
}()
var信号量=make(chan结构{},5)
var rate=make(chan结构{},10)
func init(){
//漏桶
go func(){
股票代码:=时间.NewTicker(100*时间.毫秒)
延迟停止
适用于range ticker.C{

_,确定:=如果您想要一个全局速率限制器,您需要一个地方来维护分布式状态,例如zookeeper。通常,我们不想支付开销。或者,您可以设置一个转发代理(),在其中执行速率限制。

我无法与您找到的库联系,但速率限制非常简单。您需要某种共享事务存储。每个存储桶(或速率限制)然后是一个简单的整数和一个时间值。整数是特定时间内存储桶中的滴数。每次必须应用速率限制时,减去自上次更新以来泄漏的滴数,然后添加一个,然后检查滴数是否在存储桶的容量内

我们正在使用Redis进行此类操作。要在Redis中实现此事务性,需要一个脚本(请参阅和)。例如,在SQL数据库中,一个
SELECT for UPDATE
后跟一个
UPDATE
语句将实现相同的功能。这是我们的Redis脚本:

——replicate\u命令允许我们使用TIME命令。我们依赖于精确的
--(且合理一致)时间戳。多个客户端可能具有
--不可接受的时钟漂移。
redis.replicate_命令()
本地速率=tonumber(ARGV[1])——每秒泄漏多少滴
本地上限=桶数(ARGV[2])——桶中可容纳多少滴
现在本地,uz=unpack(redis.call('TIME'))
--一个bucket由一个带有两个键的散列表示,n和t。n是
--在时间t(自纪元起的秒数)时落在铲斗中。
本地xs=redis.call('HMGET',键[1],'n','t')
本地n=tonumber(xs[1])
本地t=tonumber(xs[2])
如果类型(n)~=“数字”或类型(t)~=“数字”,则
--桶还不存在(n和t为假),或者有人搞错了
--我们的散列值。无论哪种方式,都假设桶是空的。
n、 现在t=0
结束
--清除从t开始泄漏的液滴
n=n-(现在-t)*费率
如果n<0,则
n=0
结束
--如果合适,加一滴
如果n
每秒10滴的呼叫示例,容量为10滴:

EVALSHA <SHA_IN_HEX> 1 rate-limit:my-bucket 10 10 
EVALSHA 1费率限制:我的桶10
脚本返回存储桶中的丢弃数。如果该数字等于容量,则您可以短时间睡眠并重试,也可以根据需要完全拒绝请求


请注意,脚本从不返回大于容量的值,因此在您的情况下,恢复时间不超过十分之一秒。这可能不是您所需要的,因为您正在尝试匹配第三方速率限制器。也就是说,您可能可以让存储桶溢出,从而在突发请求后获得更长的恢复时间.

如果超过600,会发生什么?大概你会得到429响应或类似的响应?如果你只是以合理的方式处理429呢?我们使用的是一个漏桶速率限制器。我不确定它是否符合你的用例,但在我们的团队中,我们发现它非常适合流式用例(我们在websocket上限制速率)。不确定它在您的使用案例中是否会同样好。免责声明:我编写了该库,但Oracle的生产软件正在使用它cloud@Flimzy我可能会发现错误,但有些服务更为关键,并且与用户请求有关,不能让用户等到限制最终重置。@jwells131313听起来不错。你能给我一些提示吗有关详细信息?github上的代码似乎只是客户端代码,不知道它如何与其他正在运行的实例共享速率限制。但您已经要求用户等待计时器重置。自己限制速率与在429上重试之间唯一的逻辑区别是网络请求的数量。当然,这一点很重要,但是,如果您希望通常处于限制之下,处理429可能会容易得多。使用带速率限制器的前向代理正是我想要的。在我的解决方案中,我现在使用基于Goproxy的Cuttle。
EVALSHA <SHA_IN_HEX> 1 rate-limit:my-bucket 10 10