使用golang服务器对第三方API进行速率限制

huangapple go评论87阅读模式
英文:

Rate limiting for a third party API with a golang server

问题

你好,我有一个使用gorilla-mux的Go后端,它使用第三方API。我有一些处理程序会向该API发送请求。我的限制是每秒5个请求。

我应该如何实现一种整体速率限制系统,其中请求会在容量可用时排队,并且只有在有空闲的五个插槽之一时才会发送?谢谢。

英文:

Hi I have a Go backend with gorilla-mux that uses a third party API. I have some handlers that make requests to this API. My limits are 5 requests a second.

How could I implement some sort of overall rate limiting system where requests are queued and sent through only when capacity is available (or one slot out of the five is free)? Thanks.

答案1

得分: 2

对于对第三方API的速率限制请求,您可以使用Golang库golang.org/x/time/rate

示例用法

package main

import (
	"context"
	"log"
	"net/http"
	"time"

	"golang.org/x/time/rate"
)

func main() {
	rl := rate.NewLimiter(rate.Every(10*time.Second), 50)
	reqURL := "https://www.google.com"
	c := http.Client{}

	req, err := http.NewRequest("GET", reqURL, nil)
	if err != nil {
		log.Fatal("failed to create request: %v", err)
	}
	for i := 0; i < 300; i++ {
		// 等待速率限制器
		err = rl.Wait(context.Background())
		if err != nil {
			log.Println("failed to wait: %v", err)
		}
		// 如果未超过速率,则进行请求
		_, err := c.Do(req)
		if err != nil {
			log.Println("failed to wait: %v", err)
		}
	}
}

重要提示!!!
这不是多个实例使用的解决方案!
如果您正在生成多个服务器,您应该考虑使用Redis来同步限制器(https://github.com/go-redis/redis_rate)。

英文:

For rate-limiting requests to the 3rd party API you can use Golang library golang.org/x/time/rate.

sample usage

package main

import (
	&quot;context&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;time&quot;

	&quot;golang.org/x/time/rate&quot;
)

func main() {
	rl := rate.NewLimiter(rate.Every(10*time.Second), 50)
	reqURL := &quot;https://www.google.com&quot;
	c := http.Client{}

	req, err := http.NewRequest(&quot;GET&quot;, reqURL, nil)
	if err != nil {
		log.Fatal(&quot;failed to create request: %v&quot;, err)
	}
	for i := 0; i &lt; 300; i++ {
		// Waiting for rate limiter
		err = rl.Wait(context.Background())
		if err != nil {
			log.Println(&quot;failed to wait: %v&quot;, err)
		}
		// and doing the requests if the rate is not exceeded
		_, err := c.Do(req)
		if err != nil {
			log.Println(&quot;failed to wait: %v&quot;, err)
		}
	}
}

IMPORTANT!!!
It's not a solution for multiple instances usage!
If you're spawning multiple servers you should think about using Redis for synchronizing limiters (https://github.com/go-redis/redis_rate).

答案2

得分: 1

也许你可以尝试这个:https://github.com/dypflying/leakybucket,假设漏桶算法的速率限制器可能适合你的场景。

英文:

maybe you can try this: https://github.com/dypflying/leakybucket, assume the leaky-bucket algorithm rate limiter may fit your scenario.

答案3

得分: 0

如果我理解正确,你有一个应用程序X,应该连接到一个API Y,而X不能每秒发送超过5个请求给Y。

这是一个复杂且不完整的场景。让我问几个问题:

  1. X的预期负载是多少?如果低于每秒5个请求...那就没问题。
  2. X的超时时间是多少?假设你每秒收到50个请求...在这种情况下,你可能需要10秒来回答一些请求,可以吗?
  3. 如果X超时,客户端会重试吗?
  4. 如果调用Y超过每秒5个请求会发生什么?
  5. Y的响应是否可缓存?
  6. 你是否有多个服务器/自动扩展?

一种可能性是在应用程序上设置速率限制器,以匹配API的限制。

另一种方法是尽可能多地调用API。如果由于请求过多而失败,你可以实现重试逻辑或放弃。

如果由于某种原因你需要非常小心地处理这个API,并且不需要运行多个实例/自动扩展,那么解决方案是在应用程序上使用速率限制器。

如果你需要运行多个实例,你需要一种集中访问该API的方法,这是非常棘手的...它是一个单点故障。你可以实现一个令牌系统,每秒只提供5个令牌。一旦你获得一个令牌,就可以访问API。这是一种可能性。

没有免费的午餐。每种解决方案都有优缺点。但是,如果你可以避免对API执行请求(如缓存结果)或者如果你只需要存储数据(并运行异步程序来处理),那么也许会更容易讨论一个更好的解决方案。

英文:

If I understand well, you have an application X that should connect to an API Y and X can’t send more than 5 requests per second to Y.

This is a complex and incomplete scenario. Let me ask few questions

  1. What is the expected load on X? If it is below 5 requests per seconds… it is ok
  2. What is the timeout on X? Imagine that you received 50 requests per second… on this scenario you may need 10 seconds to answer some requests, is it ok?
  3. In the case of a timeout in X, the client will just retry?
  4. What happens if you call Y more than 5 requests per second?
  5. Is the response from Y is cacheable?
  6. Do you have multiple servers / autoscale?

One possibility is to set a rate limiter on the application to match the limit on the API.

Another is just call the API as much as you can. If it fail because too much requests you can implement a retry logic or give up.

If you need to be very careful with this API for some reason, and you don’t need to run multiple instances / autoscale, the solution is use a rate limiter on the application.

If you need to run several instances you need something that centralizes the access to this API and this is a very delicate thing… it is a single point of failure. You can implement one token system that only delivers 5 tokens per second. Once you have a token you can access the API. It is one possibility.

There is no free lunch. Each solution has pros and cons. But if you can avoid perform requests to the API (like caching the results) ou add messages to a queue if you only need to store the data (and run an async program)… perhaps will be easier to discuss q better solution

huangapple
  • 本文由 发表于 2022年4月23日 15:52:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/71977854.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定