如何处理API的不同速率限制?

时间:2017-09-12 19:54:05

标签: elixir

我为正在运行的API构建了一个包装器,但现在该公司决定为其端点实施不同的速率限制,因为这是针对他们拥有服务器且每个服务器具有不同限制的游戏,例如,EUW服务器有一个限制,而北美有另一个,但他们为他们的玩家提供相同的信息。现在,我实现了一个GenServer来处理这些不同的速率限制,但我无法找到一种方法来处理20r / 1s或100r / 2m的开发人员密钥的速率限制。默认情况下,我实现了第一个限制,每秒20个请求,但并不总是如此。我使用ExRated来处理请求。

我希望完成的工作:一种处理开发密钥短期和长期限制的方法。

在执行请求的函数中,我有一些我认为我需要摆脱的东西,它现在只是一个占位符。

  def get(region, rest, opt) do
    url = Map.get(@endpoint, region)

    if Mix.env() == :prod do # This part
      # Enforcing the time and amount of requests per method if
      # opts provided
      opt_time = Keyword.get(opt, :time)
      opt_amount = Keyword.get(opt, :amount)

      region
      |> ExRated.check_rate(opt_time, opt_amount)
      |> parse(url, rest)
    else
      region
      |> ExRated.check_rate(time(), amount())
      |> parse(url, rest)
    end
  end

上面的else语句是我想要修复以支持两种开发限制的语句。

编辑根据第一条评论:

defmodule Godfist.LeagueRates do
  @moduledoc false

  # Handles checking the information passed and assigning the correct
  # limit to the request.

  use GenServer

  alias Godfist.HTTP

  # Rates for different servers.
  @rates [
    # "League" endpoints/servers
    euw: {300, 60_000},
    na: {270, 60_000},
    eune: {135, 60_000},
    br: {90, 60_000},
    kr: {90, 60_000},
    lan: {80, 60_000},
    las: {80, 60_000},
    tr: {60, 60_000},
    oce: {55, 60_000},
    jp: {35, 60_000},
    ru: {35, 60_000},
    # other endpoints
    match: {500, 10_000},
    matchlist: {1000, 10_000},
    champion_masteries_runes: {400, 60_000},
    static: {10, 3_600_000},
    other: {20_000, 10_000}
  ]

  # API
  def start_link,
    do: GenServer.start_link(__MODULE__, %{}, name: :league_limit)

  def handle_rate(region, rest, endpoint \\ nil) do
    GenServer.call(:league_limit, {:handle_rate, region, rest, endpoint})
  end

  # Server
  def init(state),
    do: {:ok, state}

  # This first handler is matching on the "Leagues" endpoints,
  # that's why endpoint is nil, that arg is meant to be used with
  # the other endpoints (Matches, Runes, etc...)
  def handle_call({:handle_rate, region, rest, endpoint}, _from, state)
  when is_nil(endpoint) do
    {time, amount} = Keyword.get(@rates, region)

    {:reply, HTTP.get(region, rest, time: time, amount: amount), state}
  end

  def handle_call({:handle_rate, region, rest, endpoint}, _from, state) do
    {time, amount} = Keyword.get(@rates, endpoint)

    {:reply, HTTP.get(region, rest, time: time, amount: amount), state}
  end
end

1 个答案:

答案 0 :(得分:2)

每个区域可以保留两个计数器,一个用于每秒20个请求限制,一个用于每两分钟100个请求。在发送请求之前,您可以检查是否已达到其中一个限制,如果是,则中止发送请求。

case {ExRated.check_rate("#{region}-1", 20, 1), ExRated.check_rate("#{region}-2", 100, 120)} do
  {{:ok, _}, {:ok, _}} ->
    # neither limit has been hit
  _ ->
    # one or both limits have been hit
end