我有一个微服务架构应用程序,其中有多个服务轮询外部 API。外部 API 的速率限制为每分钟 600 个请求。我如何才能让我的所有实例一起保持低于共享的 600 速率限制?
Google 只给我提供了 3 个解决方案,最有希望的是:
rate.NewLimiter 函数中的 Every 函数似乎是一个不同的导入/依赖项,我无法弄清楚它是什么目前我有一个业余的解决方案。下面的代码允许我设置每分钟的限制,它会在请求之间休眠以在一分钟内传播请求。此客户端速率限制是针对每个实例的,因此我必须通过硬编码将 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
}
}
}()
在发出 http API 请求的函数中。
rate <- struct{}{}
// check the concurrency semaphore
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
我如何才能让我的所有实例一起保持低于共享的 600 速率限制?
偏好: - 基于一个键的速率限制计数器,因此可以设置多个计数器。 - 在设定的持续时间内分散请求,这样 600 个请求不会在前 30 秒内发送,而是在整分钟持续时间内发送。
最佳答案
我无法与您找到的图书馆对话,但是 leaky bucket速率限制器非常简单。您需要某种共享事务存储。每个桶(或速率限制器)然后只是一个整数和一个时间值。整数是特定时间桶中的滴数。每次必须应用速率限制时,减去自上次更新以来泄漏的滴数,然后加一,然后检查滴数是否在桶的容量范围内。
我们正在使用 Redis 来做这类事情。要在 Redis 中实现此事务,需要一个脚本(请参阅 SCRIPT LOAD 和 EVALSHA)。例如,在 SQL 数据库中,SELECT FOR UPDATE 后跟 UPDATE 语句可以实现相同的目的。这是我们的 Redis 脚本:
-- replicate_commands allows us to use the TIME command. We depend on accurate
-- (and reasonably consistent) timestamps. Multiple clients may have
-- inacceptable clock drift.
redis.replicate_commands()
local rate = tonumber(ARGV[1]) -- how many drops leak away in one second
local cap = tonumber(ARGV[2]) -- how many drops fit in the bucket
local now, _ = unpack(redis.call('TIME'))
-- A bucket is represented by a hash with two keys, n and t. n is the number of
-- drops in the bucket at time t (seconds since epoch).
local xs = redis.call('HMGET', KEYS[1], 'n', 't')
local n = tonumber(xs[1])
local t = tonumber(xs[2])
if type(n) ~= "number" or type(t) ~= "number" then
-- The bucket doesn't exist yet (n and t are false), or someone messed with
-- our hash values. Either way, pretend the bucket is empty.
n, t = 0, now
end
-- remove drops that leaked since t
n = n - (now-t)*rate
if n < 0 then
n = 0
end
-- add one drop if it fits
if n < cap then
n = n + 1
else
n = cap
end
redis.call('HMSET', KEYS[1], 'n', n, 't', now)
redis.call('EXPIRE', KEYS[1], math.floor(n/rate) + 1)
return n
示例调用每秒 10 滴,容量为 10 滴:
EVALSHA <SHA_IN_HEX> 1 rate-limit:my-bucket 10 10
脚本返回桶中的滴数。如果该数字等于容量,您可以休眠一小段时间然后重试,或者直接拒绝请求,具体取决于您的要求。
请注意,脚本永远不会返回大于容量的值,因此在您的情况下恢复时间不超过十分之一秒。这可能不是您所需要的,因为您正在尝试匹配第三方速率限制器。 IE。你可能对桶溢出没问题,这会导致在突发请求后恢复时间更长。
关于go - 分布式出站 http 速率限制器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56827726/
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba
我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara
我试图在我的网站上实现使用Facebook登录功能,但在尝试从Facebook取回访问token时遇到障碍。这是我的代码:ifparams[:error_reason]=="user_denied"thenflash[:error]="TologinwithFacebook,youmustclick'Allow'toletthesiteaccessyourinformation"redirect_to:loginelsifparams[:code]thentoken_uri=URI.parse("https://graph.facebook.com/oauth/access_token
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
我正在尝试解析网页,但有时会收到404错误。这是我用来获取网页的代码:result=Net::HTTP::getURI.parse(URI.escape(url))如何测试result是否为404错误代码? 最佳答案 像这样重写你的代码:uri=URI.parse(url)result=Net::HTTP.start(uri.host,uri.port){|http|http.get(uri.path)}putsresult.codeputsresult.body这将打印状态码和正文。
我正在安装gitlabhq,并且在Gemfile中有对某些资源的“git://...”的引用。但是,我在公司防火墙后面,所以我必须使用http://。我可以手动编辑Gemfile,但我想知道是否有另一种方法告诉bundler使用http://作为git存储库? 最佳答案 您可以通过运行gitconfig--globalurl."https://".insteadOfgit://或通过将以下内容添加到~/.gitconfig:[url"https://"]insteadOf=git://