Очередь вызовов API, чтобы соответствовать пределу скорости
Использование Full Contact API, но они имеют ограничение скорости 300 звонков / минуту. В настоящее время у меня есть его, чтобы установить, что он выполняет вызов API при загрузке файла электронной почты в формате CSV. Я хочу поставить его в очередь так, чтобы, как только он достигнет ограничения скорости или сделал 300 звонков, он ждал 1 минуту и продолжал. Тогда я положу на это delayed_job. Как я могу это сделать? Быстрое решение заключается в использовании
sleep 60
но как мне найти его таким, чтобы он уже сделал 300 вызовов, сделать его спящим или поставить его в очередь для следующего набора?
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
hashy = row.to_hash
email = hashy["email"]
begin
Contact.create!(email: email, contact_hash: FullContact.person(email: email).to_json)
rescue FullContact::NotFound
Contact.create!(email: email, contact_hash: "Not Found")
end
end
end
1 ответ
Здесь есть несколько вопросов, о которых стоит подумать: будет ли один процесс, использующий ваш ключ API, одновременно или возможно, что несколько процессов будут запущены одновременно? Если у вас есть несколько delayed_job
рабочие, я думаю, что последнее вероятно. Я не использовал delayed_jobs
достаточно, чтобы дать вам хорошее решение, но я чувствую, что вы будете ограничены одним работником.
В настоящее время я работаю над аналогичной проблемой с API с ограничением 1 запрос каждые 0,5 секунды, максимум 1000 в день. Я еще не понял, как я хочу отслеживать ежедневное использование, но я обработал ограничение в секунду, используя потоки. Если вы можете ограничить ограничение как "1 запрос каждые 0,2 секунды", это может освободить вас от необходимости отслеживать его каждую минуту (хотя у вас все еще остается проблема отслеживания нескольких работников).
Основная идея заключается в том, что у меня есть метод запроса, который разбивает один запрос на очередь параметров запроса (на основе максимального количества объектов, разрешенных API-интерфейсом для каждого запроса), а затем другой метод выполняет итерацию по этой очереди и вызывает блок, который отправляет фактический запрос на удаленный сервер. Что-то вроде этого:
def make_multiple_requests(queue, &block)
result = []
queue.each do |request|
timer = Thread.new { sleep REQUEST_INTERVAL }
execution = Thread.new { result << yield(request) }
[timer, execution].each(&:join)
end
result
end
Чтобы использовать это:
make_multiple_requests(queue) do |request|
your_request_method_goes_here(request)
end
Основным преимуществом здесь является то, что если запрос занимает больше времени, чем допустимый интервал, вам не нужно ждать, пока sleep
чтобы закончить, и вы можете начать свой следующий запрос прямо сейчас. Это просто гарантирует, что следующий запрос не запустится, пока не пройдет хотя бы интервал. Я заметил, что, хотя интервал установлен правильно, я иногда получаю ответ "превышение квоты" от API. В этих случаях запрос повторяется после истечения соответствующего интервала.