【问题标题】:`tooManyRequests` Error in SWIFT using Amadeus API使用 Amadeus API 在 SWIFT 中出现“tooManyRequests”错误
【发布时间】:2020-11-29 08:49:28
【问题描述】:

我正在使用Amadeus API 进行航班搜索。 Amadeus API 要求请求的频率不得超过 1/100 毫秒。我使用下面的代码来限制请求频率

        for depDate in depDates{
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                AmadeusHelper.sharedInstance().searchAFlight(depAirport: depAirport, arrAirport: arrAirport, depDate: depDate, airlineCode: airlineCode, flightNumber: flightNum) { result in
                    switch result{
                    case .failure(let err):
                        print(err)
                    case .success(let flightOffer):
                        if let json = flightOffer.get(){
                            flightCandidate.offers?.append(json)
                        }
                    }
                    
                }
            }
        }

上面的for-loop 运行了 4 个循环。 searchAFlight 函数本质上是HTTP request 的包装。 asyncAfter 函数会将每个请求延迟 1 秒。我以为这就够了。但是,我仍然收到tooManyRequests 错误。在调试的时候,我是按照代码一步一步来的,发送请求需要更长的时间。请求应该分散在几分钟内,足够长以满足 100 毫秒的间隔。我在这里做错了吗?谢谢。

===========更新=========

根据Eric的cmets,我把代码改成如下

        var timeNow = DispatchTime.now() // each request is delayed with an increasing interval 
        for (index, depDate) in depDates.enumerated(){
            DispatchQueue.main.asyncAfter(deadline: timeNow + Double(1*index)) {
                AmadeusHelper.sharedInstance().searchAFlight(depAirport: depAirport, arrAirport: arrAirport, depDate: depDate, airlineCode: airlineCode, flightNumber: flightNum) { result in
                    switch result{
                    case .failure(let err):
                        print(err)
                    case .success(let flightOffer):
                        if let json = flightOffer.get(){
                            flightCandidate.offers?.append(json)
                        }
                    }
                    
                }
            }
        }

不幸的是,我仍然收到tooManyRequest 错误。

【问题讨论】:

  • DispatchQueue.main.asyncAfter(deadline: .now() + 1) 所做的只是抵消请求将 开始 的时刻,但它们仍然会按顺序执行得非常快(因为循环),一旦第一个一个已经开始(它们都在同一时间偏移)。
  • 谢谢@EricAya 你有什么建议可以按照要求的间隔执行四个请求吗?计时器?
  • 嗨@EricAya 我尝试了单个请求(不使用semaphore,但只发送一个请求),它工作正常。我会尝试semaphoretimer。但这里有一件事是,API 有时不会调用完成处理程序。您给出的示例在完成处理程序中有一个semaphore.signal()。有什么建议在这里添加一些超时逻辑吗?谢谢!

标签: swift httprequest amadeus


【解决方案1】:

您的第一次尝试是:

for depDate in depDates {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) { ... }
}

这行不通,因为您将所有这些迭代安排为从现在开始一秒,而不是彼此相隔一秒。

然后你尝试了:

for (index, depDate) in depDates.enumerated() {
    DispatchQueue.main.asyncAfter(deadline: .now() + Double(index)) { ... }
}

这可能更接近您想要的,但受制于“计时器合并”,操作系统将开始将分派的块分组/合并在一起的功能。这是一个很好的省电功能,但会避免在请求之间有延迟的愿望。此外,如果您想取消其中一些后续请求(至少不会使代码复杂一点),您将会遇到麻烦。


最简单的解决方案是采用递归模式,您可以在前一个请求的完成处理程序中触发下一个请求。

func searchFlight(at index: Int = 0) {
    guard index < depDates.count else { return }

    let depDate = depDates[index]

    AmadeusHelper.sharedInstance().searchAFlight(depDate: depDate, ...) { result in
        defer {
            if index < (depDates.count - 1) {
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in 
                    self?.searchFlight(at: index + 1)
                }
            }
        }

        switch result { ... }
    }
}

这些调用没有合并。此外,这具有以下优点,即后续请求的延迟将基于前一个请求完成的时间,而不是在前一个请求发出之后(这可能是一个问题如果预先安排一堆这样的请求)。

话虽如此,我建议您将此询问直接发送给 Amadeus。如果引入这个 100 毫秒的限制是为了防止人们通过他们的 API 挖掘他们的数据库,我不会感到惊讶。如果他们有其他未记录的技术来识别过多的请求(例如,每小时、每 24 小时的请求数等),我也不会感到惊讶。 “为什么我会收到tooManyRequest 错误”的问题最好针对他们。

【讨论】:

  • 谢谢@Rob!我很惊讶 iOS 在使用 Timer 时会执行grouping/coalescing dispatched blocksTimer 不是设计为间隔重复执行任务吗?间隔发送请求应该是一个非常常见的场景。为什么递归调用是唯一的解决方案?就我而言,这可能不起作用,因为 Amadeus API 有时不会调用完成处理程序。我不知道为什么。
  • 非常感谢您提供此解决方案。我已阅读有关此问题的小时文件和文章。大多数时候推荐Semaphore。你认为'信号量'可以解决这个问题吗?谢谢!
  • 不,我认为信号量不是一个好的解决方案。事实上,它们经常在 StackOverflow 上被讨论,但它们几乎总是错误的解决方案:阻塞线程的任何事情本质上都是低效的(工作线程的数量非常有限),并且稍有误用,就会引入死锁,阻塞主线程等。信号量的用例非常狭窄,这不是其中之一。
  • 重新合并计时器,使用重复计时器,它们不会合并。使用 Timer API 和/或 GCD 计时器,您可以选择退出合并。但是asyncAfter 并没有给你那种控制权。这都是学术性的,因为您的 100 毫秒窗口可能在前一个请求完成后开始,所以您真的希望它在前一个 完成 后开始,而不是在您启动它时开始。
  • 谢谢@Rob!我想递归函数是要走的路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-25
  • 2017-09-23
  • 2018-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多